lib.rs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690
  1. #![doc = include_str!("../README.md")]
  2. #![doc(html_logo_url = "https://avatars.githubusercontent.com/u/79236386")]
  3. #![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")]
  4. extern crate proc_macro;
  5. use layout::Layout;
  6. use nest::{Nest, NestId};
  7. use proc_macro::TokenStream;
  8. use quote::{__private::Span, format_ident, quote, ToTokens};
  9. use redirect::Redirect;
  10. use route::{Route, RouteType};
  11. use segment::RouteSegment;
  12. use syn::{parse::ParseStream, parse_macro_input, Ident, Token, Type};
  13. use proc_macro2::TokenStream as TokenStream2;
  14. use crate::{layout::LayoutId, route_tree::RouteTree};
  15. mod layout;
  16. mod nest;
  17. mod query;
  18. mod redirect;
  19. mod route;
  20. mod route_tree;
  21. mod segment;
  22. /// Derives the Routable trait for an enum of routes
  23. ///
  24. /// Each variant must:
  25. /// 1. Be struct-like with {}'s
  26. /// 2. Contain all of the dynamic parameters of the current and nested routes
  27. /// 3. Have a `#[route("route")]` attribute
  28. ///
  29. /// Route Segments:
  30. /// 1. Static Segments: "/static"
  31. /// 2. Dynamic Segments: "/:dynamic" (where dynamic has a type that is FromStr in all child Variants)
  32. /// 3. Catch all Segments: "/:..segments" (where segments has a type that is FromSegments in all child Variants)
  33. /// 4. Query Segments: "/?:..query" (where query has a type that is FromQuery in all child Variants) or "/?:query&:other_query" (where query and other_query has a type that is FromQueryArgument in all child Variants)
  34. ///
  35. /// Routes are matched:
  36. /// 1. By there specificity this order: Query Routes ("/?:query"), Static Routes ("/route"), Dynamic Routes ("/:route"), Catch All Routes ("/:..route")
  37. /// 2. By the order they are defined in the enum
  38. ///
  39. /// All features:
  40. /// ```rust, skip
  41. /// #[rustfmt::skip]
  42. /// #[derive(Clone, Debug, PartialEq, Routable)]
  43. /// enum Route {
  44. /// // Define routes with the route macro. If the name of the component is not the same as the variant, you can specify it as the second parameter
  45. /// #[route("/", IndexComponent)]
  46. /// Index {},
  47. /// // Nests with parameters have types taken from child routes
  48. /// // Everything inside the nest has the added parameter `user_id: usize`
  49. /// #[nest("/user/:user_id")]
  50. /// // All children of layouts will be rendered inside the Outlet in the layout component
  51. /// // Creates a Layout UserFrame that has the parameter `user_id: usize`
  52. /// #[layout(UserFrame)]
  53. /// // If there is a component with the name Route1, you do not need to pass in the component name
  54. /// #[route("/:dynamic?:query")]
  55. /// Route1 {
  56. /// // The type is taken from the first instance of the dynamic parameter
  57. /// user_id: usize,
  58. /// dynamic: usize,
  59. /// query: String,
  60. /// extra: String,
  61. /// },
  62. /// #[route("/hello_world")]
  63. /// // You can opt out of the layout by using the `!` prefix
  64. /// #[layout(!UserFrame)]
  65. /// Route2 { user_id: usize },
  66. /// // End layouts with #[end_layout]
  67. /// #[end_layout]
  68. /// // End nests with #[end_nest]
  69. /// #[end_nest]
  70. /// // Redirects take a path and a function that takes the parameters from the path and returns a new route
  71. /// #[redirect("/:id/user", |id: usize| Route::Route3 { dynamic: id.to_string()})]
  72. /// #[route("/:dynamic")]
  73. /// Route3 { dynamic: String },
  74. /// #[child]
  75. /// NestedRoute(NestedRoute),
  76. /// }
  77. /// ```
  78. ///
  79. /// # `#[route("path", component)]`
  80. ///
  81. /// The `#[route]` attribute is used to define a route. It takes up to 3 parameters:
  82. /// - `path`: The path to the enum variant (relative to the parent nest)
  83. /// - (optional) `component`: The component to render when the route is matched. If not specified, the name of the variant is used
  84. ///
  85. /// Routes are the most basic attribute. They allow you to define a route and the component to render when the route is matched. The component must take all dynamic parameters of the route and all parent nests.
  86. /// The next variant will be tied to the component. If you link to that variant, the component will be rendered.
  87. ///
  88. /// ```rust, skip
  89. /// #[derive(Clone, Debug, PartialEq, Routable)]
  90. /// enum Route {
  91. /// // Define routes that renders the IndexComponent
  92. /// // The Index component will be rendered when the route is matched (e.g. when the user navigates to /)
  93. /// #[route("/", Index)]
  94. /// Index {},
  95. /// }
  96. /// ```
  97. ///
  98. /// # `#[redirect("path", function)]`
  99. ///
  100. /// The `#[redirect]` attribute is used to define a redirect. It takes 2 parameters:
  101. /// - `path`: The path to the enum variant (relative to the parent nest)
  102. /// - `function`: A function that takes the parameters from the path and returns a new route
  103. ///
  104. /// ```rust, skip
  105. /// #[derive(Clone, Debug, PartialEq, Routable)]
  106. /// enum Route {
  107. /// // Redirects the /:id route to the Index route
  108. /// #[redirect("/:id", |_: usize| Route::Index {})]
  109. /// #[route("/", Index)]
  110. /// Index {},
  111. /// }
  112. /// ```
  113. ///
  114. /// Redirects allow you to redirect a route to another route. The function must take all dynamic parameters of the route and all parent nests.
  115. ///
  116. /// # `#[nest("path")]`
  117. ///
  118. /// The `#[nest]` attribute is used to define a nest. It takes 1 parameter:
  119. /// - `path`: The path to the nest (relative to the parent nest)
  120. ///
  121. /// Nests effect all nests, routes and redirects defined until the next `#[end_nest]` attribute. All children of nests are relative to the nest route and must include all dynamic parameters of the nest.
  122. ///
  123. /// ```rust, skip
  124. /// #[derive(Clone, Debug, PartialEq, Routable)]
  125. /// enum Route {
  126. /// // Nests all child routes in the /blog route
  127. /// #[nest("/blog")]
  128. /// // This is at /blog/:id
  129. /// #[redirect("/:id", |_: usize| Route::Index {})]
  130. /// // This is at /blog
  131. /// #[route("/", Index)]
  132. /// Index {},
  133. /// }
  134. /// ```
  135. ///
  136. /// # `#[end_nest]`
  137. ///
  138. /// The `#[end_nest]` attribute is used to end a nest. It takes no parameters.
  139. ///
  140. /// ```rust, skip
  141. /// #[derive(Clone, Debug, PartialEq, Routable)]
  142. /// enum Route {
  143. /// #[nest("/blog")]
  144. /// // This is at /blog/:id
  145. /// #[redirect("/:id", |_: usize| Route::Index {})]
  146. /// // This is at /blog
  147. /// #[route("/", Index)]
  148. /// Index {},
  149. /// // Ends the nest
  150. /// #[end_nest]
  151. /// // This is at /
  152. /// #[route("/")]
  153. /// Home {},
  154. /// }
  155. /// ```
  156. ///
  157. /// # `#[layout(component)]`
  158. ///
  159. /// The `#[layout]` attribute is used to define a layout. It takes 2 parameters:
  160. /// - `component`: The component to render when the route is matched. If not specified, the name of the variant is used
  161. ///
  162. /// The layout component allows you to wrap all children of the layout in a component. The child routes are rendered in the Outlet of the layout component. The layout component must take all dynamic parameters of the nests it is nested in.
  163. ///
  164. /// ```rust, skip
  165. /// #[derive(Clone, Debug, PartialEq, Routable)]
  166. /// enum Route {
  167. /// #[layout(BlogFrame)]
  168. /// #[redirect("/:id", |_: usize| Route::Index {})]
  169. /// // Index will be rendered in the Outlet of the BlogFrame component
  170. /// #[route("/", Index)]
  171. /// Index {},
  172. /// }
  173. /// ```
  174. ///
  175. /// # `#[end_layout]`
  176. ///
  177. /// The `#[end_layout]` attribute is used to end a layout. It takes no parameters.
  178. ///
  179. /// ```rust, skip
  180. /// #[derive(Clone, Debug, PartialEq, Routable)]
  181. /// enum Route {
  182. /// #[layout(BlogFrame)]
  183. /// #[redirect("/:id", |_: usize| Route::Index {})]
  184. /// // Index will be rendered in the Outlet of the BlogFrame component
  185. /// #[route("/", Index)]
  186. /// Index {},
  187. /// // Ends the layout
  188. /// #[end_layout]
  189. /// // This will be rendered standalone
  190. /// #[route("/")]
  191. /// Home {},
  192. /// }
  193. /// ```
  194. #[proc_macro_derive(
  195. Routable,
  196. attributes(route, nest, end_nest, layout, end_layout, redirect, child)
  197. )]
  198. pub fn routable(input: TokenStream) -> TokenStream {
  199. let routes_enum = parse_macro_input!(input as syn::ItemEnum);
  200. let route_enum = match RouteEnum::parse(routes_enum) {
  201. Ok(route_enum) => route_enum,
  202. Err(err) => return err.to_compile_error().into(),
  203. };
  204. let error_type = route_enum.error_type();
  205. let parse_impl = route_enum.parse_impl();
  206. let display_impl = route_enum.impl_display();
  207. let routable_impl = route_enum.routable_impl();
  208. (quote! {
  209. #error_type
  210. #display_impl
  211. #routable_impl
  212. #parse_impl
  213. })
  214. .into()
  215. }
  216. struct RouteEnum {
  217. name: Ident,
  218. redirects: Vec<Redirect>,
  219. routes: Vec<Route>,
  220. nests: Vec<Nest>,
  221. layouts: Vec<Layout>,
  222. site_map: Vec<SiteMapSegment>,
  223. }
  224. impl RouteEnum {
  225. fn parse(data: syn::ItemEnum) -> syn::Result<Self> {
  226. let name = &data.ident;
  227. let mut site_map = Vec::new();
  228. let mut site_map_stack: Vec<Vec<SiteMapSegment>> = Vec::new();
  229. let mut routes = Vec::new();
  230. let mut redirects = Vec::new();
  231. let mut layouts: Vec<Layout> = Vec::new();
  232. let mut layout_stack = Vec::new();
  233. let mut nests = Vec::new();
  234. let mut nest_stack = Vec::new();
  235. for variant in &data.variants {
  236. let mut excluded = Vec::new();
  237. // Apply the any nesting attributes in order
  238. for attr in &variant.attrs {
  239. if attr.path().is_ident("nest") {
  240. let mut children_routes = Vec::new();
  241. {
  242. // add all of the variants of the enum to the children_routes until we hit an end_nest
  243. let mut level = 0;
  244. 'o: for variant in &data.variants {
  245. children_routes.push(variant.fields.clone());
  246. for attr in &variant.attrs {
  247. if attr.path().is_ident("nest") {
  248. level += 1;
  249. } else if attr.path().is_ident("end_nest") {
  250. level -= 1;
  251. if level < 0 {
  252. break 'o;
  253. }
  254. }
  255. }
  256. }
  257. }
  258. let nest_index = nests.len();
  259. let parser = |input: ParseStream| {
  260. Nest::parse(
  261. input,
  262. children_routes
  263. .iter()
  264. .filter_map(|f: &syn::Fields| match f {
  265. syn::Fields::Named(fields) => Some(fields.clone()),
  266. _ => None,
  267. })
  268. .collect(),
  269. nest_index,
  270. )
  271. };
  272. let nest = attr.parse_args_with(parser)?;
  273. // add the current segment to the site map stack
  274. let segments: Vec<_> = nest
  275. .segments
  276. .iter()
  277. .map(|seg| {
  278. let segment_type = seg.into();
  279. SiteMapSegment {
  280. segment_type,
  281. children: Vec::new(),
  282. }
  283. })
  284. .collect();
  285. if !segments.is_empty() {
  286. site_map_stack.push(segments);
  287. }
  288. nests.push(nest);
  289. nest_stack.push(NestId(nest_index));
  290. } else if attr.path().is_ident("end_nest") {
  291. nest_stack.pop();
  292. // pop the current nest segment off the stack and add it to the parent or the site map
  293. if let Some(segment) = site_map_stack.pop() {
  294. let children = site_map_stack
  295. .last_mut()
  296. .map(|seg| &mut seg.last_mut().unwrap().children)
  297. .unwrap_or(&mut site_map);
  298. // Turn the list of segments in the segments stack into a tree
  299. let mut iter = segment.into_iter().rev();
  300. let mut current = iter.next().unwrap();
  301. for mut segment in iter {
  302. segment.children.push(current);
  303. current = segment;
  304. }
  305. children.push(current);
  306. }
  307. } else if attr.path().is_ident("layout") {
  308. let parser = |input: ParseStream| {
  309. let bang: Option<Token![!]> = input.parse().ok();
  310. let exclude = bang.is_some();
  311. Ok((exclude, Layout::parse(input, nest_stack.clone())?))
  312. };
  313. let (exclude, layout): (bool, Layout) = attr.parse_args_with(parser)?;
  314. if exclude {
  315. let Some(layout_index) = layouts.iter().position(|l| l.comp == layout.comp)
  316. else {
  317. return Err(syn::Error::new(
  318. Span::call_site(),
  319. "Attempted to exclude a layout that does not exist",
  320. ));
  321. };
  322. excluded.push(LayoutId(layout_index));
  323. } else {
  324. let layout_index = layouts.len();
  325. layouts.push(layout);
  326. layout_stack.push(LayoutId(layout_index));
  327. }
  328. } else if attr.path().is_ident("end_layout") {
  329. layout_stack.pop();
  330. } else if attr.path().is_ident("redirect") {
  331. let parser = |input: ParseStream| {
  332. Redirect::parse(input, nest_stack.clone(), redirects.len())
  333. };
  334. let redirect = attr.parse_args_with(parser)?;
  335. redirects.push(redirect);
  336. }
  337. }
  338. let active_nests = nest_stack.clone();
  339. let mut active_layouts = layout_stack.clone();
  340. active_layouts.retain(|&id| !excluded.contains(&id));
  341. let route = Route::parse(active_nests, active_layouts, variant.clone())?;
  342. // add the route to the site map
  343. let mut segment = SiteMapSegment::new(&route.segments);
  344. if let RouteType::Child(child) = &route.ty {
  345. let new_segment = SiteMapSegment {
  346. segment_type: SegmentType::Child(child.ty.clone()),
  347. children: Vec::new(),
  348. };
  349. match &mut segment {
  350. Some(segment) => {
  351. fn set_last_child_to(
  352. segment: &mut SiteMapSegment,
  353. new_segment: SiteMapSegment,
  354. ) {
  355. if let Some(last) = segment.children.last_mut() {
  356. set_last_child_to(last, new_segment);
  357. } else {
  358. segment.children = vec![new_segment];
  359. }
  360. }
  361. set_last_child_to(segment, new_segment);
  362. }
  363. None => {
  364. segment = Some(new_segment);
  365. }
  366. }
  367. }
  368. if let Some(segment) = segment {
  369. let parent = site_map_stack.last_mut();
  370. let children = match parent {
  371. Some(parent) => &mut parent.last_mut().unwrap().children,
  372. None => &mut site_map,
  373. };
  374. children.push(segment);
  375. }
  376. routes.push(route);
  377. }
  378. // pop any remaining site map segments
  379. while let Some(segment) = site_map_stack.pop() {
  380. let children = site_map_stack
  381. .last_mut()
  382. .map(|seg| &mut seg.last_mut().unwrap().children)
  383. .unwrap_or(&mut site_map);
  384. // Turn the list of segments in the segments stack into a tree
  385. let mut iter = segment.into_iter().rev();
  386. let mut current = iter.next().unwrap();
  387. for mut segment in iter {
  388. segment.children.push(current);
  389. current = segment;
  390. }
  391. children.push(current);
  392. }
  393. let myself = Self {
  394. name: name.clone(),
  395. routes,
  396. redirects,
  397. nests,
  398. layouts,
  399. site_map,
  400. };
  401. Ok(myself)
  402. }
  403. fn impl_display(&self) -> TokenStream2 {
  404. let mut display_match = Vec::new();
  405. for route in &self.routes {
  406. display_match.push(route.display_match(&self.nests));
  407. }
  408. let name = &self.name;
  409. quote! {
  410. impl std::fmt::Display for #name {
  411. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  412. #[allow(unused)]
  413. match self {
  414. #(#display_match)*
  415. }
  416. Ok(())
  417. }
  418. }
  419. }
  420. }
  421. fn parse_impl(&self) -> TokenStream2 {
  422. let tree = RouteTree::new(&self.routes, &self.nests, &self.redirects);
  423. let name = &self.name;
  424. let error_name = format_ident!("{}MatchError", self.name);
  425. let tokens = tree.roots.iter().map(|&id| {
  426. let route = tree.get(id).unwrap();
  427. route.to_tokens(&self.nests, &tree, self.name.clone(), error_name.clone())
  428. });
  429. quote! {
  430. impl<'a> core::convert::TryFrom<&'a str> for #name {
  431. type Error = <Self as std::str::FromStr>::Err;
  432. fn try_from(s: &'a str) -> Result<Self, Self::Error> {
  433. s.parse()
  434. }
  435. }
  436. impl std::str::FromStr for #name {
  437. type Err = dioxus_router::routable::RouteParseError<#error_name>;
  438. fn from_str(s: &str) -> Result<Self, Self::Err> {
  439. let route = s;
  440. let (route, _hash) = route.split_once('#').unwrap_or((route, ""));
  441. let (route, query) = route.split_once('?').unwrap_or((route, ""));
  442. let query = dioxus_router::exports::urlencoding::decode(query).unwrap_or(query.into());
  443. let mut segments = route.split('/').map(|s| dioxus_router::exports::urlencoding::decode(s).unwrap_or(s.into()));
  444. // skip the first empty segment
  445. if s.starts_with('/') {
  446. let _ = segments.next();
  447. }
  448. else {
  449. // if this route does not start with a slash, it is not a valid route
  450. return Err(dioxus_router::routable::RouteParseError {
  451. attempted_routes: Vec::new(),
  452. });
  453. }
  454. let mut errors = Vec::new();
  455. #(#tokens)*
  456. Err(dioxus_router::routable::RouteParseError {
  457. attempted_routes: errors,
  458. })
  459. }
  460. }
  461. }
  462. }
  463. fn error_name(&self) -> Ident {
  464. Ident::new(&(self.name.to_string() + "MatchError"), Span::call_site())
  465. }
  466. fn error_type(&self) -> TokenStream2 {
  467. let match_error_name = self.error_name();
  468. let mut type_defs = Vec::new();
  469. let mut error_variants = Vec::new();
  470. let mut display_match = Vec::new();
  471. for route in &self.routes {
  472. let route_name = &route.route_name;
  473. let error_name = route.error_ident();
  474. let route_str = &route.route;
  475. error_variants.push(quote! { #route_name(#error_name) });
  476. display_match.push(quote! { Self::#route_name(err) => write!(f, "Route '{}' ('{}') did not match:\n{}", stringify!(#route_name), #route_str, err)? });
  477. type_defs.push(route.error_type());
  478. }
  479. for nest in &self.nests {
  480. let error_variant = nest.error_variant();
  481. let error_name = nest.error_ident();
  482. let route_str = &nest.route;
  483. error_variants.push(quote! { #error_variant(#error_name) });
  484. display_match.push(quote! { Self::#error_variant(err) => write!(f, "Nest '{}' ('{}') did not match:\n{}", stringify!(#error_name), #route_str, err)? });
  485. type_defs.push(nest.error_type());
  486. }
  487. for redirect in &self.redirects {
  488. let error_variant = redirect.error_variant();
  489. let error_name = redirect.error_ident();
  490. let route_str = &redirect.route;
  491. error_variants.push(quote! { #error_variant(#error_name) });
  492. display_match.push(quote! { Self::#error_variant(err) => write!(f, "Redirect '{}' ('{}') did not match:\n{}", stringify!(#error_name), #route_str, err)? });
  493. type_defs.push(redirect.error_type());
  494. }
  495. quote! {
  496. #(#type_defs)*
  497. #[allow(non_camel_case_types)]
  498. #[derive(Debug, PartialEq)]
  499. pub enum #match_error_name {
  500. #(#error_variants),*
  501. }
  502. impl std::fmt::Display for #match_error_name {
  503. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  504. match self {
  505. #(#display_match),*
  506. }
  507. Ok(())
  508. }
  509. }
  510. }
  511. }
  512. fn routable_impl(&self) -> TokenStream2 {
  513. let name = &self.name;
  514. let site_map = &self.site_map;
  515. let mut matches = Vec::new();
  516. // Collect all routes matches
  517. for route in &self.routes {
  518. matches.push(route.routable_match(&self.layouts, &self.nests));
  519. }
  520. quote! {
  521. impl dioxus_router::routable::Routable for #name where Self: Clone {
  522. const SITE_MAP: &'static [dioxus_router::routable::SiteMapSegment] = &[
  523. #(#site_map,)*
  524. ];
  525. fn render<'a>(&self, cx: &'a dioxus::prelude::ScopeState, level: usize) -> dioxus::prelude::Element<'a> {
  526. let myself = self.clone();
  527. match (level, myself) {
  528. #(#matches)*
  529. _ => None
  530. }
  531. }
  532. }
  533. }
  534. }
  535. }
  536. struct SiteMapSegment {
  537. pub segment_type: SegmentType,
  538. pub children: Vec<SiteMapSegment>,
  539. }
  540. impl SiteMapSegment {
  541. fn new(segments: &[RouteSegment]) -> Option<Self> {
  542. let mut current = None;
  543. // walk backwards through the new segments, adding children as we go
  544. for segment in segments.iter().rev() {
  545. let segment_type = segment.into();
  546. let mut segment = SiteMapSegment {
  547. segment_type,
  548. children: Vec::new(),
  549. };
  550. // if we have a current segment, add it as a child
  551. if let Some(current) = current.take() {
  552. segment.children.push(current)
  553. }
  554. current = Some(segment);
  555. }
  556. current
  557. }
  558. }
  559. impl ToTokens for SiteMapSegment {
  560. fn to_tokens(&self, tokens: &mut TokenStream2) {
  561. let segment_type = &self.segment_type;
  562. let children = if let SegmentType::Child(ty) = &self.segment_type {
  563. quote! { #ty::SITE_MAP }
  564. } else {
  565. let children = self
  566. .children
  567. .iter()
  568. .map(|child| child.to_token_stream())
  569. .collect::<Vec<_>>();
  570. quote! {
  571. &[
  572. #(#children,)*
  573. ]
  574. }
  575. };
  576. tokens.extend(quote! {
  577. dioxus_router::routable::SiteMapSegment {
  578. segment_type: #segment_type,
  579. children: #children,
  580. }
  581. });
  582. }
  583. }
  584. enum SegmentType {
  585. Static(String),
  586. Dynamic(String),
  587. CatchAll(String),
  588. Child(Type),
  589. }
  590. impl ToTokens for SegmentType {
  591. fn to_tokens(&self, tokens: &mut TokenStream2) {
  592. match self {
  593. SegmentType::Static(s) => {
  594. tokens.extend(quote! { dioxus_router::routable::SegmentType::Static(#s) })
  595. }
  596. SegmentType::Dynamic(s) => {
  597. tokens.extend(quote! { dioxus_router::routable::SegmentType::Dynamic(#s) })
  598. }
  599. SegmentType::CatchAll(s) => {
  600. tokens.extend(quote! { dioxus_router::routable::SegmentType::CatchAll(#s) })
  601. }
  602. SegmentType::Child(_) => {
  603. tokens.extend(quote! { dioxus_router::routable::SegmentType::Child })
  604. }
  605. }
  606. }
  607. }
  608. impl<'a> From<&'a RouteSegment> for SegmentType {
  609. fn from(value: &'a RouteSegment) -> Self {
  610. match value {
  611. RouteSegment::Static(s) => SegmentType::Static(s.to_string()),
  612. RouteSegment::Dynamic(s, _) => SegmentType::Dynamic(s.to_string()),
  613. RouteSegment::CatchAll(s, _) => SegmentType::CatchAll(s.to_string()),
  614. }
  615. }
  616. }