routable.rs 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. //! # Routable
  2. #![allow(non_snake_case)]
  3. use dioxus::prelude::*;
  4. use std::{fmt::Display, str::FromStr};
  5. /// An error that occurs when parsing a route
  6. #[derive(Debug, PartialEq)]
  7. pub struct RouteParseError<E: std::fmt::Display> {
  8. /// The attempted routes that failed to match
  9. pub attempted_routes: Vec<E>,
  10. }
  11. impl<E: std::fmt::Display> std::fmt::Display for RouteParseError<E> {
  12. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  13. write!(f, "Route did not match:\nAttempted Matches:\n")?;
  14. for (i, route) in self.attempted_routes.iter().enumerate() {
  15. writeln!(f, "{}) {route}", i + 1)?;
  16. }
  17. Ok(())
  18. }
  19. }
  20. /// Something that can be created from a query string.
  21. ///
  22. /// This trait needs to be implemented if you want to turn a query string into a struct.
  23. ///
  24. /// A working example can be found in the `examples` folder in the root package under `query_segments_demo`
  25. pub trait FromQuery {
  26. /// Create an instance of `Self` from a query string
  27. fn from_query(query: &str) -> Self;
  28. }
  29. impl<T: for<'a> From<&'a str>> FromQuery for T {
  30. fn from_query(query: &str) -> Self {
  31. T::from(&*urlencoding::decode(query).expect("Failed to decode url encoding"))
  32. }
  33. }
  34. /// Something that can be created from a route segment
  35. pub trait FromRouteSegment: Sized {
  36. /// The error that can occur when parsing a route segment
  37. type Err;
  38. /// Create an instance of `Self` from a route segment
  39. fn from_route_segment(route: &str) -> Result<Self, Self::Err>;
  40. }
  41. impl<T: FromStr> FromRouteSegment for T
  42. where
  43. <T as FromStr>::Err: std::fmt::Display,
  44. {
  45. type Err = <T as FromStr>::Err;
  46. fn from_route_segment(route: &str) -> Result<Self, Self::Err> {
  47. match urlencoding::decode(route) {
  48. Ok(segment) => T::from_str(&segment),
  49. Err(err) => {
  50. log::error!("Failed to decode url encoding: {}", err);
  51. T::from_str(route)
  52. }
  53. }
  54. }
  55. }
  56. #[test]
  57. fn full_circle() {
  58. let route = "testing 1234 hello world";
  59. assert_eq!(String::from_route_segment(route).unwrap(), route);
  60. }
  61. /// Something that can be converted to route segments
  62. pub trait ToRouteSegments {
  63. /// Display the route segments
  64. fn display_route_segements(self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result;
  65. }
  66. impl<I, T: std::fmt::Display> ToRouteSegments for I
  67. where
  68. I: IntoIterator<Item = T>,
  69. {
  70. fn display_route_segements(self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  71. for segment in self {
  72. write!(f, "/")?;
  73. let segment = segment.to_string();
  74. match urlencoding::decode(&segment) {
  75. Ok(segment) => write!(f, "{}", segment)?,
  76. Err(err) => {
  77. log::error!("Failed to decode url encoding: {}", err);
  78. write!(f, "{}", segment)?
  79. }
  80. }
  81. }
  82. Ok(())
  83. }
  84. }
  85. #[test]
  86. fn to_route_segments() {
  87. struct DisplaysRoute;
  88. impl std::fmt::Display for DisplaysRoute {
  89. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  90. let segments = vec!["hello", "world"];
  91. segments.display_route_segements(f)
  92. }
  93. }
  94. assert_eq!(DisplaysRoute.to_string(), "/hello/world");
  95. }
  96. /// Something that can be created from route segments
  97. pub trait FromRouteSegments: Sized {
  98. /// The error that can occur when parsing route segments
  99. type Err;
  100. /// Create an instance of `Self` from route segments
  101. fn from_route_segments(segments: &[&str]) -> Result<Self, Self::Err>;
  102. }
  103. impl<I: std::iter::FromIterator<String>> FromRouteSegments for I {
  104. type Err = <String as FromRouteSegment>::Err;
  105. fn from_route_segments(segments: &[&str]) -> Result<Self, Self::Err> {
  106. segments
  107. .iter()
  108. .map(|s| String::from_route_segment(s))
  109. .collect()
  110. }
  111. }
  112. /// Something that can be:
  113. /// 1. Converted from a route
  114. /// 2. Converted to a route
  115. /// 3. Rendered as a component
  116. ///
  117. /// This trait can be derived using the `#[derive(Routable)]` macro
  118. pub trait Routable: std::fmt::Display + std::str::FromStr + Clone + 'static {
  119. /// The error that can occur when parsing a route
  120. const SITE_MAP: &'static [SiteMapSegment];
  121. /// Render the route at the given level
  122. fn render<'a>(&self, cx: &'a ScopeState, level: usize) -> Element<'a>;
  123. /// Checks if this route is a child of the given route
  124. ///
  125. /// # Example
  126. /// ```rust
  127. /// use dioxus_router::prelude::*;
  128. /// use dioxus::prelude::*;
  129. ///
  130. /// #[inline_props]
  131. /// fn Home(cx: Scope) -> Element { todo!() }
  132. /// #[inline_props]
  133. /// fn About(cx: Scope) -> Element { todo!() }
  134. ///
  135. /// #[derive(Routable, Clone, PartialEq, Debug)]
  136. /// enum Route {
  137. /// #[route("/")]
  138. /// Home {},
  139. /// #[route("/about")]
  140. /// About {},
  141. /// }
  142. ///
  143. /// let route = Route::About {};
  144. /// let parent = Route::Home {};
  145. /// assert!(route.is_child_of(&parent));
  146. /// ```
  147. fn is_child_of(&self, other: &Self) -> bool {
  148. let self_str = self.to_string();
  149. let self_str = self_str.trim_matches('/');
  150. let other_str = other.to_string();
  151. let other_str = other_str.trim_matches('/');
  152. if other_str.is_empty() {
  153. return true;
  154. }
  155. let self_segments = self_str.split('/');
  156. let other_segments = other_str.split('/');
  157. for (self_seg, other_seg) in self_segments.zip(other_segments) {
  158. if self_seg != other_seg {
  159. return false;
  160. }
  161. }
  162. true
  163. }
  164. /// Get the parent route of this route
  165. ///
  166. /// # Example
  167. /// ```rust
  168. /// use dioxus_router::prelude::*;
  169. /// use dioxus::prelude::*;
  170. ///
  171. /// #[inline_props]
  172. /// fn Home(cx: Scope) -> Element { todo!() }
  173. /// #[inline_props]
  174. /// fn About(cx: Scope) -> Element { todo!() }
  175. ///
  176. /// #[derive(Routable, Clone, PartialEq, Debug)]
  177. /// enum Route {
  178. /// #[route("/")]
  179. /// Home {},
  180. /// #[route("/about")]
  181. /// About {},
  182. /// }
  183. ///
  184. /// let route = Route::About {};
  185. /// let parent = route.parent().unwrap();
  186. /// assert_eq!(parent, Route::Home {});
  187. /// ```
  188. fn parent(&self) -> Option<Self> {
  189. let as_str = self.to_string();
  190. let as_str = as_str.trim_matches('/');
  191. let segments = as_str.split('/');
  192. let segment_count = segments.clone().count();
  193. let new_route = segments
  194. .take(segment_count - 1)
  195. .fold(String::new(), |mut acc, segment| {
  196. acc.push('/');
  197. acc.push_str(segment);
  198. acc
  199. });
  200. Self::from_str(&new_route).ok()
  201. }
  202. /// Gets a list of all static routes
  203. fn static_routes() -> Vec<Self> {
  204. Self::SITE_MAP
  205. .iter()
  206. .flat_map(|segment| segment.flatten())
  207. .filter_map(|route| {
  208. if route
  209. .iter()
  210. .all(|segment| matches!(segment, SegmentType::Static(_)))
  211. {
  212. Self::from_str(
  213. &route
  214. .iter()
  215. .map(|segment| match segment {
  216. SegmentType::Static(s) => s.to_string(),
  217. _ => unreachable!(),
  218. })
  219. .collect::<Vec<_>>()
  220. .join("/"),
  221. )
  222. .ok()
  223. } else {
  224. None
  225. }
  226. })
  227. .collect()
  228. }
  229. }
  230. trait RoutableFactory {
  231. type Err: std::fmt::Display;
  232. type Routable: Routable + FromStr<Err = Self::Err>;
  233. }
  234. impl<R: Routable + FromStr> RoutableFactory for R
  235. where
  236. <R as FromStr>::Err: std::fmt::Display,
  237. {
  238. type Err = <R as FromStr>::Err;
  239. type Routable = R;
  240. }
  241. trait RouteRenderable: std::fmt::Display + 'static {
  242. fn render<'a>(&self, cx: &'a ScopeState, level: usize) -> Element<'a>;
  243. }
  244. impl<R: Routable> RouteRenderable for R
  245. where
  246. <R as FromStr>::Err: std::fmt::Display,
  247. {
  248. fn render<'a>(&self, cx: &'a ScopeState, level: usize) -> Element<'a> {
  249. self.render(cx, level)
  250. }
  251. }
  252. /// A type erased map of the site structurens
  253. #[derive(Debug, Clone, PartialEq)]
  254. pub struct SiteMapSegment {
  255. /// The type of the route segment
  256. pub segment_type: SegmentType,
  257. /// The children of the route segment
  258. pub children: &'static [SiteMapSegment],
  259. }
  260. impl SiteMapSegment {
  261. /// Take a map of the site structure and flatten it into a vector of routes
  262. pub fn flatten(&self) -> Vec<Vec<SegmentType>> {
  263. let mut routes = Vec::new();
  264. self.flatten_inner(&mut routes, Vec::new());
  265. routes
  266. }
  267. fn flatten_inner(&self, routes: &mut Vec<Vec<SegmentType>>, current: Vec<SegmentType>) {
  268. let mut current = current;
  269. current.push(self.segment_type.clone());
  270. if self.children.is_empty() {
  271. routes.push(current);
  272. } else {
  273. for child in self.children {
  274. child.flatten_inner(routes, current.clone());
  275. }
  276. }
  277. }
  278. }
  279. /// The type of a route segment
  280. #[derive(Debug, Clone, PartialEq)]
  281. pub enum SegmentType {
  282. /// A static route segment
  283. Static(&'static str),
  284. /// A dynamic route segment
  285. Dynamic(&'static str),
  286. /// A catch all route segment
  287. CatchAll(&'static str),
  288. /// A child router
  289. Child,
  290. }
  291. impl Display for SegmentType {
  292. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  293. match &self {
  294. SegmentType::Static(s) => write!(f, "/{}", s),
  295. SegmentType::Child => Ok(()),
  296. SegmentType::Dynamic(s) => write!(f, "/:{}", s),
  297. SegmentType::CatchAll(s) => write!(f, "/:...{}", s),
  298. }
  299. }
  300. }