routable.rs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822
  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. This trait must be implemented for any type that is spread into the query segment like `#[route("/?:..query")]`.
  23. ///
  24. ///
  25. /// **This trait is automatically implemented for any types that implement `From<&str>`.**
  26. ///
  27. /// ```rust
  28. /// use dioxus::prelude::*;
  29. ///
  30. /// #[derive(Routable, Clone, PartialEq, Debug)]
  31. /// enum Route {
  32. /// // FromQuery must be implemented for any types you spread into the query segment
  33. /// #[route("/?:..query")]
  34. /// Home {
  35. /// query: CustomQuery
  36. /// },
  37. /// }
  38. ///
  39. /// #[derive(Default, Clone, PartialEq, Debug)]
  40. /// struct CustomQuery {
  41. /// count: i32,
  42. /// }
  43. ///
  44. /// // We implement From<&str> for CustomQuery so that FromQuery is implemented automatically
  45. /// impl From<&str> for CustomQuery {
  46. /// fn from(query: &str) -> Self {
  47. /// CustomQuery {
  48. /// count: query.parse().unwrap_or(0),
  49. /// }
  50. /// }
  51. /// }
  52. ///
  53. /// // We also need to implement Display for CustomQuery which will be used to format the query string into the URL
  54. /// impl std::fmt::Display for CustomQuery {
  55. /// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  56. /// write!(f, "{}", self.count)
  57. /// }
  58. /// }
  59. ///
  60. /// # #[component]
  61. /// # fn Home(query: CustomQuery) -> Element {
  62. /// # unimplemented!()
  63. /// # }
  64. /// ```
  65. #[rustversion::attr(
  66. since(1.78.0),
  67. diagnostic::on_unimplemented(
  68. message = "`FromQuery` is not implemented for `{Self}`",
  69. label = "spread query",
  70. note = "FromQuery is automatically implemented for types that implement `From<&str>`. You need to either implement From<&str> or implement FromQuery manually."
  71. )
  72. )]
  73. pub trait FromQuery {
  74. /// Create an instance of `Self` from a query string.
  75. fn from_query(query: &str) -> Self;
  76. }
  77. impl<T: for<'a> From<&'a str>> FromQuery for T {
  78. fn from_query(query: &str) -> Self {
  79. T::from(query)
  80. }
  81. }
  82. /// Something that can be created from a query argument. This trait must be implemented for any type that is used as a query argument like `#[route("/?:query")]`.
  83. ///
  84. /// **This trait is automatically implemented for any types that implement `FromStr` and `Default`.**
  85. ///
  86. /// ```rust
  87. /// use dioxus::prelude::*;
  88. ///
  89. /// #[derive(Routable, Clone, PartialEq, Debug)]
  90. /// enum Route {
  91. /// // FromQuerySegment must be implemented for any types you use in the query segment
  92. /// // When you don't spread the query, you can parse multiple values form the query
  93. /// // This url will be in the format `/?query=123&other=456`
  94. /// #[route("/?:query&:other")]
  95. /// Home {
  96. /// query: CustomQuery,
  97. /// other: i32,
  98. /// },
  99. /// }
  100. ///
  101. /// // We can derive Default for CustomQuery
  102. /// // If the router fails to parse the query value, it will use the default value instead
  103. /// #[derive(Default, Clone, PartialEq, Debug)]
  104. /// struct CustomQuery {
  105. /// count: i32,
  106. /// }
  107. ///
  108. /// // We implement FromStr for CustomQuery so that FromQuerySegment is implemented automatically
  109. /// impl std::str::FromStr for CustomQuery {
  110. /// type Err = <i32 as std::str::FromStr>::Err;
  111. ///
  112. /// fn from_str(query: &str) -> Result<Self, Self::Err> {
  113. /// Ok(CustomQuery {
  114. /// count: query.parse()?,
  115. /// })
  116. /// }
  117. /// }
  118. ///
  119. /// // We also need to implement Display for CustomQuery which will be used to format the query string into the URL
  120. /// impl std::fmt::Display for CustomQuery {
  121. /// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  122. /// write!(f, "{}", self.count)
  123. /// }
  124. /// }
  125. ///
  126. /// # #[component]
  127. /// # fn Home(query: CustomQuery, other: i32) -> Element {
  128. /// # unimplemented!()
  129. /// # }
  130. /// ```
  131. #[rustversion::attr(
  132. since(1.78.0),
  133. diagnostic::on_unimplemented(
  134. message = "`FromQueryArgument` is not implemented for `{Self}`",
  135. label = "query argument",
  136. note = "FromQueryArgument is automatically implemented for types that implement `FromStr` and `Default`. You need to either implement FromStr and Default or implement FromQueryArgument manually."
  137. )
  138. )]
  139. pub trait FromQueryArgument: Default {
  140. /// The error that can occur when parsing a query argument.
  141. type Err;
  142. /// Create an instance of `Self` from a query string.
  143. fn from_query_argument(argument: &str) -> Result<Self, Self::Err>;
  144. }
  145. impl<T: Default + FromStr> FromQueryArgument for T
  146. where
  147. <T as FromStr>::Err: Display,
  148. {
  149. type Err = <T as FromStr>::Err;
  150. fn from_query_argument(argument: &str) -> Result<Self, Self::Err> {
  151. match T::from_str(argument) {
  152. Ok(result) => Ok(result),
  153. Err(err) => {
  154. tracing::error!("Failed to parse query argument: {}", err);
  155. Err(err)
  156. }
  157. }
  158. }
  159. }
  160. /// Something that can be created from an entire hash fragment. This must be implemented for any type that is used as a hash fragment like `#[route("/#:hash_fragment")]`.
  161. ///
  162. ///
  163. /// **This trait is automatically implemented for any types that implement `FromStr` and `Default`.**
  164. ///
  165. /// # Example
  166. ///
  167. /// ```rust
  168. /// use dioxus::prelude::*;
  169. ///
  170. /// #[derive(Routable, Clone)]
  171. /// #[rustfmt::skip]
  172. /// enum Route {
  173. /// // State is stored in the url hash
  174. /// #[route("/#:url_hash")]
  175. /// Home {
  176. /// url_hash: State,
  177. /// },
  178. /// }
  179. ///
  180. /// #[component]
  181. /// fn Home(url_hash: State) -> Element {
  182. /// unimplemented!()
  183. /// }
  184. ///
  185. ///
  186. /// #[derive(Clone, PartialEq, Default)]
  187. /// struct State {
  188. /// count: usize,
  189. /// other_count: usize
  190. /// }
  191. ///
  192. /// // The hash segment will be displayed as a string (this will be url encoded automatically)
  193. /// impl std::fmt::Display for State {
  194. /// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  195. /// write!(f, "{}-{}", self.count, self.other_count)
  196. /// }
  197. /// }
  198. ///
  199. /// // We need to parse the hash fragment into a struct from the string (this will be url decoded automatically)
  200. /// impl FromHashFragment for State {
  201. /// fn from_hash_fragment(hash: &str) -> Self {
  202. /// let Some((first, second)) = hash.split_once('-') else {
  203. /// // URL fragment parsing shouldn't fail. You can return a default value if you want
  204. /// return Default::default();
  205. /// };
  206. ///
  207. /// let first = first.parse().unwrap();
  208. /// let second = second.parse().unwrap();
  209. ///
  210. /// State {
  211. /// count: first,
  212. /// other_count: second,
  213. /// }
  214. /// }
  215. /// }
  216. /// ```
  217. #[rustversion::attr(
  218. since(1.78.0),
  219. diagnostic::on_unimplemented(
  220. message = "`FromHashFragment` is not implemented for `{Self}`",
  221. label = "hash fragment",
  222. note = "FromHashFragment is automatically implemented for types that implement `FromStr` and `Default`. You need to either implement FromStr and Default or implement FromHashFragment manually."
  223. )
  224. )]
  225. pub trait FromHashFragment {
  226. /// Create an instance of `Self` from a hash fragment.
  227. fn from_hash_fragment(hash: &str) -> Self;
  228. }
  229. impl<T> FromHashFragment for T
  230. where
  231. T: FromStr + Default,
  232. T::Err: std::fmt::Display,
  233. {
  234. fn from_hash_fragment(hash: &str) -> Self {
  235. match T::from_str(hash) {
  236. Ok(value) => value,
  237. Err(err) => {
  238. tracing::error!("Failed to parse hash fragment: {}", err);
  239. Default::default()
  240. }
  241. }
  242. }
  243. }
  244. /// Something that can be created from a single route segment. This must be implemented for any type that is used as a route segment like `#[route("/:route_segment")]`.
  245. ///
  246. ///
  247. /// **This trait is automatically implemented for any types that implement `FromStr` and `Default`.**
  248. ///
  249. /// ```rust
  250. /// use dioxus::prelude::*;
  251. ///
  252. /// #[derive(Routable, Clone, PartialEq, Debug)]
  253. /// enum Route {
  254. /// // FromRouteSegment must be implemented for any types you use in the route segment
  255. /// // When you don't spread the route, you can parse multiple values from the route
  256. /// // This url will be in the format `/123/456`
  257. /// #[route("/:route_segment_one/:route_segment_two")]
  258. /// Home {
  259. /// route_segment_one: CustomRouteSegment,
  260. /// route_segment_two: i32,
  261. /// },
  262. /// }
  263. ///
  264. /// // We can derive Default for CustomRouteSegment
  265. /// // If the router fails to parse the route segment, it will use the default value instead
  266. /// #[derive(Default, PartialEq, Clone, Debug)]
  267. /// struct CustomRouteSegment {
  268. /// count: i32,
  269. /// }
  270. ///
  271. /// // We implement FromStr for CustomRouteSegment so that FromRouteSegment is implemented automatically
  272. /// impl std::str::FromStr for CustomRouteSegment {
  273. /// type Err = <i32 as std::str::FromStr>::Err;
  274. ///
  275. /// fn from_str(route_segment: &str) -> Result<Self, Self::Err> {
  276. /// Ok(CustomRouteSegment {
  277. /// count: route_segment.parse()?,
  278. /// })
  279. /// }
  280. /// }
  281. ///
  282. /// // We also need to implement Display for CustomRouteSegment which will be used to format the route segment into the URL
  283. /// impl std::fmt::Display for CustomRouteSegment {
  284. /// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  285. /// write!(f, "{}", self.count)
  286. /// }
  287. /// }
  288. ///
  289. /// # #[component]
  290. /// # fn Home(route_segment_one: CustomRouteSegment, route_segment_two: i32) -> Element {
  291. /// # unimplemented!()
  292. /// # }
  293. /// ```
  294. #[rustversion::attr(
  295. since(1.78.0),
  296. diagnostic::on_unimplemented(
  297. message = "`FromRouteSegment` is not implemented for `{Self}`",
  298. label = "route segment",
  299. note = "FromRouteSegment is automatically implemented for types that implement `FromStr` and `Default`. You need to either implement FromStr and Default or implement FromRouteSegment manually."
  300. )
  301. )]
  302. pub trait FromRouteSegment: Sized {
  303. /// The error that can occur when parsing a route segment.
  304. type Err;
  305. /// Create an instance of `Self` from a route segment.
  306. fn from_route_segment(route: &str) -> Result<Self, Self::Err>;
  307. }
  308. impl<T: FromStr> FromRouteSegment for T
  309. where
  310. <T as FromStr>::Err: Display,
  311. {
  312. type Err = <T as FromStr>::Err;
  313. fn from_route_segment(route: &str) -> Result<Self, Self::Err> {
  314. T::from_str(route)
  315. }
  316. }
  317. #[test]
  318. fn full_circle() {
  319. let route = "testing 1234 hello world";
  320. assert_eq!(String::from_route_segment(route).unwrap(), route);
  321. }
  322. /// Something that can be converted into multiple route segments. This must be implemented for any type that is spread into the route segment like `#[route("/:..route_segments")]`.
  323. ///
  324. ///
  325. /// **This trait is automatically implemented for any types that implement `IntoIterator<Item=impl Display>`.**
  326. ///
  327. /// ```rust
  328. /// use dioxus::prelude::*;
  329. ///
  330. /// #[derive(Routable, Clone, PartialEq, Debug)]
  331. /// enum Route {
  332. /// // FromRouteSegments must be implemented for any types you use in the route segment
  333. /// // When you spread the route, you can parse multiple values from the route
  334. /// // This url will be in the format `/123/456/789`
  335. /// #[route("/:..numeric_route_segments")]
  336. /// Home {
  337. /// numeric_route_segments: NumericRouteSegments,
  338. /// },
  339. /// }
  340. ///
  341. /// // We can derive Default for NumericRouteSegments
  342. /// // If the router fails to parse the route segment, it will use the default value instead
  343. /// #[derive(Default, PartialEq, Clone, Debug)]
  344. /// struct NumericRouteSegments {
  345. /// numbers: Vec<i32>,
  346. /// }
  347. ///
  348. /// // Implement ToRouteSegments for NumericRouteSegments so that we can display the route segments
  349. /// impl ToRouteSegments for NumericRouteSegments {
  350. /// fn display_route_segments(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  351. /// for number in &self.numbers {
  352. /// write!(f, "/{}", number)?;
  353. /// }
  354. /// Ok(())
  355. /// }
  356. /// }
  357. ///
  358. /// // We also need to parse the route segments with `FromRouteSegments`
  359. /// impl FromRouteSegments for NumericRouteSegments {
  360. /// type Err = <i32 as std::str::FromStr>::Err;
  361. ///
  362. /// fn from_route_segments(segments: &[&str]) -> Result<Self, Self::Err> {
  363. /// let mut numbers = Vec::new();
  364. /// for segment in segments {
  365. /// numbers.push(segment.parse()?);
  366. /// }
  367. /// Ok(NumericRouteSegments { numbers })
  368. /// }
  369. /// }
  370. ///
  371. /// # #[component]
  372. /// # fn Home(numeric_route_segments: NumericRouteSegments) -> Element {
  373. /// # unimplemented!()
  374. /// # }
  375. /// ```
  376. pub trait ToRouteSegments {
  377. /// Display the route segments with each route segment separated by a `/`. This should not start with a `/`.
  378. ///
  379. fn display_route_segments(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result;
  380. }
  381. // Implement ToRouteSegments for any type that can turn &self into an iterator of &T where T: Display
  382. impl<I, T: Display> ToRouteSegments for I
  383. where
  384. for<'a> &'a I: IntoIterator<Item = &'a T>,
  385. {
  386. fn display_route_segments(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  387. for segment in self {
  388. write!(f, "/")?;
  389. let segment = segment.to_string();
  390. let encoded = urlencoding::encode(&segment);
  391. write!(f, "{}", encoded)?;
  392. }
  393. Ok(())
  394. }
  395. }
  396. #[test]
  397. fn to_route_segments() {
  398. struct DisplaysRoute;
  399. impl Display for DisplaysRoute {
  400. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  401. let segments = vec!["hello", "world"];
  402. segments.display_route_segments(f)
  403. }
  404. }
  405. assert_eq!(DisplaysRoute.to_string(), "/hello/world");
  406. }
  407. /// Something that can be created from multiple route segments. This must be implemented for any type that is spread into the route segment like `#[route("/:..route_segments")]`.
  408. ///
  409. ///
  410. /// **This trait is automatically implemented for any types that implement `FromIterator<impl Display>`.**
  411. ///
  412. /// ```rust
  413. /// use dioxus::prelude::*;
  414. ///
  415. /// #[derive(Routable, Clone, PartialEq, Debug)]
  416. /// enum Route {
  417. /// // FromRouteSegments must be implemented for any types you use in the route segment
  418. /// // When you spread the route, you can parse multiple values from the route
  419. /// // This url will be in the format `/123/456/789`
  420. /// #[route("/:..numeric_route_segments")]
  421. /// Home {
  422. /// numeric_route_segments: NumericRouteSegments,
  423. /// },
  424. /// }
  425. ///
  426. /// // We can derive Default for NumericRouteSegments
  427. /// // If the router fails to parse the route segment, it will use the default value instead
  428. /// #[derive(Default, Clone, PartialEq, Debug)]
  429. /// struct NumericRouteSegments {
  430. /// numbers: Vec<i32>,
  431. /// }
  432. ///
  433. /// // Implement ToRouteSegments for NumericRouteSegments so that we can display the route segments
  434. /// impl ToRouteSegments for NumericRouteSegments {
  435. /// fn display_route_segments(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  436. /// for number in &self.numbers {
  437. /// write!(f, "/{}", number)?;
  438. /// }
  439. /// Ok(())
  440. /// }
  441. /// }
  442. ///
  443. /// // We also need to parse the route segments with `FromRouteSegments`
  444. /// impl FromRouteSegments for NumericRouteSegments {
  445. /// type Err = <i32 as std::str::FromStr>::Err;
  446. ///
  447. /// fn from_route_segments(segments: &[&str]) -> Result<Self, Self::Err> {
  448. /// let mut numbers = Vec::new();
  449. /// for segment in segments {
  450. /// numbers.push(segment.parse()?);
  451. /// }
  452. /// Ok(NumericRouteSegments { numbers })
  453. /// }
  454. /// }
  455. ///
  456. /// # #[component]
  457. /// # fn Home(numeric_route_segments: NumericRouteSegments) -> Element {
  458. /// # unimplemented!()
  459. /// # }
  460. /// ```
  461. #[rustversion::attr(
  462. since(1.78.0),
  463. diagnostic::on_unimplemented(
  464. message = "`FromRouteSegments` is not implemented for `{Self}`",
  465. label = "spread route segments",
  466. note = "FromRouteSegments is automatically implemented for types that implement `FromIterator` with an `Item` type that implements `Display`. You need to either implement FromIterator or implement FromRouteSegments manually."
  467. )
  468. )]
  469. pub trait FromRouteSegments: Sized {
  470. /// The error that can occur when parsing route segments.
  471. type Err: std::fmt::Display;
  472. /// Create an instance of `Self` from route segments.
  473. ///
  474. /// NOTE: This method must parse the output of `ToRouteSegments::display_route_segments` into the type `Self`.
  475. fn from_route_segments(segments: &[&str]) -> Result<Self, Self::Err>;
  476. }
  477. impl<I: std::iter::FromIterator<String>> FromRouteSegments for I {
  478. type Err = <String as FromRouteSegment>::Err;
  479. fn from_route_segments(segments: &[&str]) -> Result<Self, Self::Err> {
  480. segments
  481. .iter()
  482. .map(|s| String::from_route_segment(s))
  483. .collect()
  484. }
  485. }
  486. /// A flattened version of [`Routable::SITE_MAP`].
  487. /// This essentially represents a `Vec<Vec<SegmentType>>`, which you can collect it into.
  488. type SiteMapFlattened<'a> = FlatMap<
  489. Iter<'a, SiteMapSegment>,
  490. Vec<Vec<SegmentType>>,
  491. fn(&SiteMapSegment) -> Vec<Vec<SegmentType>>,
  492. >;
  493. /// The Routable trait is implemented for types that can be converted to and from a route and be rendered as a page.
  494. ///
  495. /// A Routable object is something that can be:
  496. /// 1. Converted from a route.
  497. /// 2. Converted to a route.
  498. /// 3. Rendered as a component.
  499. ///
  500. /// This trait should generally be derived using the [`dioxus_router_macro::Routable`] macro which has more information about the syntax.
  501. ///
  502. /// ## Example
  503. /// ```rust
  504. /// use dioxus::prelude::*;
  505. ///
  506. /// fn App() -> Element {
  507. /// rsx! {
  508. /// Router::<Route> { }
  509. /// }
  510. /// }
  511. ///
  512. /// // Routes are generally enums that derive `Routable`
  513. /// #[derive(Routable, Clone, PartialEq, Debug)]
  514. /// enum Route {
  515. /// // Each enum has an associated url
  516. /// #[route("/")]
  517. /// Home {},
  518. /// // Routes can include dynamic segments that are parsed from the url
  519. /// #[route("/blog/:blog_id")]
  520. /// Blog { blog_id: usize },
  521. /// // Or query segments that are parsed from the url
  522. /// #[route("/edit?:blog_id")]
  523. /// Edit { blog_id: usize },
  524. /// // Or hash segments that are parsed from the url
  525. /// #[route("/hashtag/#:hash")]
  526. /// Hash { hash: String },
  527. /// }
  528. ///
  529. /// // Each route variant defaults to rendering a component of the same name
  530. /// #[component]
  531. /// fn Home() -> Element {
  532. /// rsx! {
  533. /// h1 { "Home" }
  534. /// }
  535. /// }
  536. ///
  537. /// // If the variant has dynamic parameters, those are passed to the component
  538. /// #[component]
  539. /// fn Blog(blog_id: usize) -> Element {
  540. /// rsx! {
  541. /// h1 { "Blog" }
  542. /// }
  543. /// }
  544. ///
  545. /// #[component]
  546. /// fn Edit(blog_id: usize) -> Element {
  547. /// rsx! {
  548. /// h1 { "Edit" }
  549. /// }
  550. /// }
  551. ///
  552. /// #[component]
  553. /// fn Hash(hash: String) -> Element {
  554. /// rsx! {
  555. /// h1 { "Hashtag #{hash}" }
  556. /// }
  557. /// }
  558. /// ```
  559. #[rustversion::attr(
  560. since(1.78.0),
  561. diagnostic::on_unimplemented(
  562. message = "`Routable` is not implemented for `{Self}`",
  563. label = "Route",
  564. note = "Routable should generally be derived using the `#[derive(Routable)]` macro."
  565. )
  566. )]
  567. pub trait Routable: FromStr + Display + Clone + 'static {
  568. /// The error that can occur when parsing a route.
  569. const SITE_MAP: &'static [SiteMapSegment];
  570. /// Render the route at the given level
  571. fn render(&self, level: usize) -> Element;
  572. /// Checks if this route is a child of the given route.
  573. ///
  574. /// # Example
  575. /// ```rust
  576. /// use dioxus_router::prelude::*;
  577. /// use dioxus::prelude::*;
  578. ///
  579. /// #[component]
  580. /// fn Home() -> Element { VNode::empty() }
  581. /// #[component]
  582. /// fn About() -> Element { VNode::empty() }
  583. ///
  584. /// #[derive(Routable, Clone, PartialEq, Debug)]
  585. /// enum Route {
  586. /// #[route("/")]
  587. /// Home {},
  588. /// #[route("/about")]
  589. /// About {},
  590. /// }
  591. ///
  592. /// let route = Route::About {};
  593. /// let parent = Route::Home {};
  594. /// assert!(route.is_child_of(&parent));
  595. /// ```
  596. fn is_child_of(&self, other: &Self) -> bool {
  597. let self_str = self.to_string();
  598. let self_str = self_str
  599. .split_once('#')
  600. .map(|(route, _)| route)
  601. .unwrap_or(&self_str);
  602. let self_str = self_str
  603. .split_once('?')
  604. .map(|(route, _)| route)
  605. .unwrap_or(self_str);
  606. let self_str = self_str.trim_end_matches('/');
  607. let other_str = other.to_string();
  608. let other_str = other_str
  609. .split_once('#')
  610. .map(|(route, _)| route)
  611. .unwrap_or(&other_str);
  612. let other_str = other_str
  613. .split_once('?')
  614. .map(|(route, _)| route)
  615. .unwrap_or(other_str);
  616. let other_str = other_str.trim_end_matches('/');
  617. let mut self_segments = self_str.split('/');
  618. let mut other_segments = other_str.split('/');
  619. loop {
  620. match (self_segments.next(), other_segments.next()) {
  621. // If the two routes are the same length, or this route has less segments, then this segment
  622. // cannot be the child of the other segment
  623. (None, Some(_)) | (None, None) => {
  624. return false;
  625. }
  626. // If two segments are not the same, then this segment cannot be the child of the other segment
  627. (Some(self_seg), Some(other_seg)) => {
  628. if self_seg != other_seg {
  629. return false;
  630. }
  631. }
  632. // If the other route has less segments, then this route is the child of the other route
  633. (Some(_), None) => break,
  634. }
  635. }
  636. true
  637. }
  638. /// Get the parent route of this route.
  639. ///
  640. /// # Example
  641. /// ```rust
  642. /// use dioxus_router::prelude::*;
  643. /// use dioxus::prelude::*;
  644. ///
  645. /// #[component]
  646. /// fn Home() -> Element { VNode::empty() }
  647. /// #[component]
  648. /// fn About() -> Element { VNode::empty() }
  649. ///
  650. /// #[derive(Routable, Clone, PartialEq, Debug)]
  651. /// enum Route {
  652. /// #[route("/home")]
  653. /// Home {},
  654. /// #[route("/home/about")]
  655. /// About {},
  656. /// }
  657. ///
  658. /// let route = Route::About {};
  659. /// let parent = route.parent().unwrap();
  660. /// assert_eq!(parent, Route::Home {});
  661. /// ```
  662. fn parent(&self) -> Option<Self> {
  663. let as_str = self.to_string();
  664. let (route_and_query, _) = as_str.split_once('#').unwrap_or((&as_str, ""));
  665. let (route, _) = route_and_query
  666. .split_once('?')
  667. .unwrap_or((route_and_query, ""));
  668. let route = route.trim_end_matches('/');
  669. let segments = route.split_inclusive('/');
  670. let segment_count = segments.clone().count();
  671. let new_route: String = segments.take(segment_count.saturating_sub(1)).collect();
  672. Self::from_str(&new_route).ok()
  673. }
  674. /// Returns a flattened version of [`Self::SITE_MAP`].
  675. fn flatten_site_map<'a>() -> SiteMapFlattened<'a> {
  676. Self::SITE_MAP.iter().flat_map(SiteMapSegment::flatten)
  677. }
  678. /// Gets a list of all the static routes.
  679. /// Example static route: `#[route("/static/route")]`
  680. fn static_routes() -> Vec<Self> {
  681. Self::flatten_site_map()
  682. .filter_map(|segments| {
  683. let mut route = String::new();
  684. for segment in segments.iter() {
  685. match segment {
  686. SegmentType::Static(s) => {
  687. route.push('/');
  688. route.push_str(s)
  689. }
  690. SegmentType::Child => {}
  691. _ => return None,
  692. }
  693. }
  694. route.parse().ok()
  695. })
  696. .collect()
  697. }
  698. }
  699. /// A type erased map of the site structure.
  700. #[derive(Debug, Clone, PartialEq)]
  701. pub struct SiteMapSegment {
  702. /// The type of the route segment.
  703. pub segment_type: SegmentType,
  704. /// The children of the route segment.
  705. pub children: &'static [SiteMapSegment],
  706. }
  707. impl SiteMapSegment {
  708. /// Take a map of the site structure and flatten it into a vector of routes.
  709. pub fn flatten(&self) -> Vec<Vec<SegmentType>> {
  710. let mut routes = Vec::new();
  711. self.flatten_inner(&mut routes, Vec::new());
  712. routes
  713. }
  714. fn flatten_inner(&self, routes: &mut Vec<Vec<SegmentType>>, current: Vec<SegmentType>) {
  715. let mut current = current;
  716. current.push(self.segment_type.clone());
  717. if self.children.is_empty() {
  718. routes.push(current);
  719. } else {
  720. for child in self.children {
  721. child.flatten_inner(routes, current.clone());
  722. }
  723. }
  724. }
  725. }
  726. /// The type of a route segment.
  727. #[derive(Debug, Clone, PartialEq)]
  728. #[non_exhaustive]
  729. pub enum SegmentType {
  730. /// A static route segment.
  731. Static(&'static str),
  732. /// A dynamic route segment.
  733. Dynamic(&'static str),
  734. /// A catch all route segment.
  735. CatchAll(&'static str),
  736. /// A child router.
  737. Child,
  738. }
  739. impl SegmentType {
  740. /// Try to convert this segment into a static segment.
  741. pub fn to_static(&self) -> Option<&'static str> {
  742. match self {
  743. SegmentType::Static(s) => Some(*s),
  744. _ => None,
  745. }
  746. }
  747. /// Try to convert this segment into a dynamic segment.
  748. pub fn to_dynamic(&self) -> Option<&'static str> {
  749. match self {
  750. SegmentType::Dynamic(s) => Some(*s),
  751. _ => None,
  752. }
  753. }
  754. /// Try to convert this segment into a catch all segment.
  755. pub fn to_catch_all(&self) -> Option<&'static str> {
  756. match self {
  757. SegmentType::CatchAll(s) => Some(*s),
  758. _ => None,
  759. }
  760. }
  761. /// Try to convert this segment into a child segment.
  762. pub fn to_child(&self) -> Option<()> {
  763. match self {
  764. SegmentType::Child => Some(()),
  765. _ => None,
  766. }
  767. }
  768. }
  769. impl Display for SegmentType {
  770. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  771. match &self {
  772. SegmentType::Static(s) => write!(f, "/{}", s),
  773. SegmentType::Child => Ok(()),
  774. SegmentType::Dynamic(s) => write!(f, "/:{}", s),
  775. SegmentType::CatchAll(s) => write!(f, "/:..{}", s),
  776. }
  777. }
  778. }