routable.rs 11 KB

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