lib.rs 25 KB

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