diff --git a/routing/src/lib.rs b/routing/src/lib.rs index ebf64ee73..7aa9b25cb 100644 --- a/routing/src/lib.rs +++ b/routing/src/lib.rs @@ -8,5 +8,6 @@ mod static_route; pub use generate_route_list::*; pub use method::*; pub use router::*; +pub use routing_utils::*; pub use ssr_mode::*; pub use static_route::*; diff --git a/routing_utils/src/location/browser.rs b/routing/src/location/browser.rs similarity index 100% rename from routing_utils/src/location/browser.rs rename to routing/src/location/browser.rs diff --git a/routing_utils/src/location/mod.rs b/routing/src/location/mod.rs similarity index 100% rename from routing_utils/src/location/mod.rs rename to routing/src/location/mod.rs diff --git a/routing_utils/src/location/server.rs b/routing/src/location/server.rs similarity index 100% rename from routing_utils/src/location/server.rs rename to routing/src/location/server.rs diff --git a/routing/src/router.rs b/routing/src/router.rs index f7ecf8ef4..88745d8cf 100644 --- a/routing/src/router.rs +++ b/routing/src/router.rs @@ -3,21 +3,25 @@ use core::marker::PhantomData; use either_of::Either; use routing_utils::{ location::Location, - matching::{MatchNestedRoutes, PossibleRouteMatch, Routes}, + matching::{MatchInterface, MatchNestedRoutes, PossibleRouteMatch, Routes}, }; use std::borrow::Cow; use tachys::{ + html::attribute::Attribute, hydration::Cursor, renderer::Renderer, ssr::StreamBuilder, - view::{either::EitherState, Position, PositionState, Render, RenderHtml}, + view::{ + add_attr::AddAnyAttr, either::EitherState, Position, PositionState, + Render, RenderHtml, + }, }; #[derive(Debug)] pub struct Router { base: Option>, location: Loc, - routes: Routes, + pub routes: Routes, fallback: FallbackFn, rndr: PhantomData, } @@ -74,21 +78,52 @@ where } } -impl Render +trait ChooseView { + type Output; + + fn choose(self) -> Self::Output; +} + +impl ChooseView for F +where + F: Fn() -> View, +{ + type Output = View; + + fn choose(self) -> Self::Output { + self() + } +} + +impl ChooseView for Either +where + FnA: Fn() -> A, + FnB: Fn() -> B, +{ + type Output = Either; + + fn choose(self) -> Self::Output { + match self { + Either::Left(f) => Either::Left(f()), + Either::Right(f) => Either::Right(f()), + } + } +} + +impl Render for Router where Loc: Location, FallbackFn: Fn() -> Fallback, Fallback: Render, for<'a> Children: MatchNestedRoutes<'a>, - for<'a> >::Match: core::fmt::Debug, + for<'a> <>::Match as MatchInterface<'a>>::View: + ChooseView, + View: Render, Rndr: Renderer, { - type State = EitherState< - >::State, - >::State, - Rndr, - >; + type State = + EitherState>::State, Rndr>; type FallibleState = (); fn build(self) -> Self::State { @@ -98,14 +133,31 @@ where .as_ref() .map(|url| self.routes.match_route(url.path())) { - Ok(Some(matched)) => Either::Left(format!("{matched:#?}")), + Ok(Some(matched)) => { + let view = matched.to_view(); + let view = view.choose(); + Either::Left(view) + } _ => Either::Right((self.fallback)()), } .build() } fn rebuild(self, state: &mut Self::State) { - todo!() + let new = match self + .location + .current() + .as_ref() + .map(|url| self.routes.match_route(url.path())) + { + Ok(Some(matched)) => { + let view = matched.to_view(); + let view = view.choose(); + Either::Left(view) + } + _ => Either::Right((self.fallback)()), + }; + new.rebuild(state); } fn try_build(self) -> tachys::error::Result { @@ -119,6 +171,69 @@ where todo!() } } + +impl RenderHtml + for Router +where + Loc: Location, + FallbackFn: Fn() -> Fallback, + Fallback: RenderHtml, + for<'a> Children: MatchNestedRoutes<'a>, + for<'a> <>::Match as MatchInterface<'a>>::View: + ChooseView, + View: Render, + Rndr: Renderer, +{ + // TODO probably pick a max length here + const MIN_LENGTH: usize = Fallback::MIN_LENGTH; + + fn to_html_with_buf(self, buf: &mut String, position: &mut Position) { + todo!() + } + + fn hydrate( + self, + cursor: &Cursor, + position: &PositionState, + ) -> Self::State { + todo!() + } +} + +impl AddAnyAttr + for Router +where + Loc: Location, + FallbackFn: Fn() -> Fallback, + Fallback: Render, + for<'a> Children: MatchNestedRoutes<'a>, + for<'a> <>::Match as MatchInterface<'a>>::View: + ChooseView, + Rndr: Renderer, + Router: RenderHtml, +{ + type Output> = Self; + + fn add_any_attr>( + self, + attr: NewAttr, + ) -> Self::Output + where + Self::Output: RenderHtml, + { + self + } + + fn add_any_attr_by_ref>( + self, + attr: &NewAttr, + ) -> Self::Output + where + Self::Output: RenderHtml, + { + self + } +} /* impl RenderHtml for Router diff --git a/routing_utils/src/matching/horizontal/mod.rs b/routing_utils/src/horizontal/mod.rs similarity index 100% rename from routing_utils/src/matching/horizontal/mod.rs rename to routing_utils/src/horizontal/mod.rs diff --git a/routing_utils/src/matching/horizontal/param_segments.rs b/routing_utils/src/horizontal/param_segments.rs similarity index 97% rename from routing_utils/src/matching/horizontal/param_segments.rs rename to routing_utils/src/horizontal/param_segments.rs index 7c584edc2..5155c2586 100644 --- a/routing_utils/src/matching/horizontal/param_segments.rs +++ b/routing_utils/src/horizontal/param_segments.rs @@ -85,7 +85,8 @@ impl PossibleRouteMatch for WildcardSegment { #[cfg(test)] mod tests { use super::PossibleRouteMatch; - use crate::matching::{ParamSegment, StaticSegment, WildcardSegment}; + use crate::{ParamSegment, StaticSegment, WildcardSegment}; + use alloc::vec::Vec; #[test] fn single_param_match() { diff --git a/routing_utils/src/matching/horizontal/static_segment.rs b/routing_utils/src/horizontal/static_segment.rs similarity index 99% rename from routing_utils/src/matching/horizontal/static_segment.rs rename to routing_utils/src/horizontal/static_segment.rs index 2238001e2..19d4707e0 100644 --- a/routing_utils/src/matching/horizontal/static_segment.rs +++ b/routing_utils/src/horizontal/static_segment.rs @@ -78,6 +78,7 @@ impl PossibleRouteMatch for StaticSegment { #[cfg(test)] mod tests { use super::{PossibleRouteMatch, StaticSegment}; + use alloc::vec::Vec; #[test] fn single_static_match() { diff --git a/routing_utils/src/matching/horizontal/tuples.rs b/routing_utils/src/horizontal/tuples.rs similarity index 99% rename from routing_utils/src/matching/horizontal/tuples.rs rename to routing_utils/src/horizontal/tuples.rs index 6fb966bb3..49c6114c6 100644 --- a/routing_utils/src/matching/horizontal/tuples.rs +++ b/routing_utils/src/horizontal/tuples.rs @@ -1,4 +1,5 @@ use super::{PartialPathMatch, PathSegment, PossibleRouteMatch}; +use alloc::vec::Vec; use core::iter::Chain; macro_rules! chain_types { diff --git a/routing_utils/src/lib.rs b/routing_utils/src/lib.rs index 9818f23d9..93f2d70b5 100644 --- a/routing_utils/src/lib.rs +++ b/routing_utils/src/lib.rs @@ -1,10 +1,388 @@ -//#![no_std] +#![no_std] #[macro_use] extern crate alloc; -pub mod location; -pub mod matching; -pub mod params; mod path_segment; +use alloc::vec::Vec; pub use path_segment::*; +mod horizontal; +mod nested; +mod vertical; +use crate::PathSegment; +use alloc::borrow::Cow; +pub use horizontal::*; +pub use nested::*; +pub use vertical::*; + +#[derive(Debug)] +pub struct Routes { + base: Option>, + children: Children, +} + +impl Routes { + pub fn new(children: Children) -> Self { + Self { + base: None, + children, + } + } + + pub fn new_with_base( + children: Children, + base: impl Into>, + ) -> Self { + Self { + base: Some(base.into()), + children, + } + } +} + +impl<'a, Children> Routes +where + Children: MatchNestedRoutes<'a>, +{ + pub fn match_route(&'a self, path: &'a str) -> Option { + let path = match &self.base { + None => path, + Some(base) => { + let (base, path) = if base.starts_with('/') { + (base.trim_start_matches('/'), path.trim_start_matches('/')) + } else { + (base.as_ref(), path) + }; + match path.strip_prefix(base) { + Some(path) => path, + None => return None, + } + } + }; + + let (matched, remaining) = self.children.match_nested(path); + let matched = matched?; + + if !remaining.is_empty() { + None + } else { + Some(matched) + } + } + + pub fn generate_routes( + &'a self, + ) -> ( + Option<&str>, + impl IntoIterator> + 'a, + ) { + (self.base.as_deref(), self.children.generate_routes()) + } +} + +pub trait MatchInterface<'a> { + type Params: IntoIterator; + type Child; + type View; + + fn to_params(&self) -> Self::Params; + + fn to_child(&'a self) -> Self::Child; + + fn to_view(&self) -> Self::View; +} + +pub trait MatchNestedRoutes<'a> { + type Data; + type Match: MatchInterface<'a>; + + fn match_nested(&'a self, path: &'a str) -> (Option, &'a str); + + fn generate_routes( + &self, + ) -> impl IntoIterator> + '_; +} + +#[cfg(test)] +mod tests { + use super::{NestedRoute, ParamSegment, Routes}; + use crate::{MatchInterface, PathSegment, StaticSegment, WildcardSegment}; + use alloc::vec::Vec; + + #[test] + pub fn matches_single_root_route() { + let routes = Routes::new(NestedRoute { + segments: StaticSegment("/"), + children: (), + data: (), + view: || (), + }); + let matched = routes.match_route("/"); + assert!(matched.is_some()); + let matched = routes.match_route(""); + assert!(matched.is_some()); + let (base, paths) = routes.generate_routes(); + assert_eq!(base, None); + let paths = paths.into_iter().collect::>(); + assert_eq!(paths, vec![vec![PathSegment::Static("/".into())]]); + } + + #[test] + pub fn matches_nested_route() { + let routes = Routes::new(NestedRoute { + segments: StaticSegment(""), + children: NestedRoute { + segments: (StaticSegment("author"), StaticSegment("contact")), + children: (), + data: (), + view: "Contact Me", + }, + data: (), + view: "Home", + }); + + // route generation + let (base, paths) = routes.generate_routes(); + assert_eq!(base, None); + let paths = paths.into_iter().collect::>(); + assert_eq!( + paths, + vec![vec![ + PathSegment::Static("".into()), + PathSegment::Static("author".into()), + PathSegment::Static("contact".into()) + ]] + ); + + let matched = routes.match_route("/author/contact").unwrap(); + assert_eq!(matched.matched(), ""); + assert_eq!(matched.to_child().matched(), "/author/contact"); + + let view = matched.to_view(); + assert_eq!(*view, "Home"); + assert_eq!(*matched.to_child().to_view(), "Contact Me"); + } + + #[test] + pub fn does_not_match_incomplete_route() { + let routes = Routes::new(NestedRoute { + segments: StaticSegment(""), + children: NestedRoute { + segments: (StaticSegment("author"), StaticSegment("contact")), + children: (), + data: (), + view: "Contact Me", + }, + data: (), + view: "Home", + }); + let matched = routes.match_route("/"); + assert!(matched.is_none()); + } + + #[test] + pub fn chooses_between_nested_routes() { + let routes = Routes::new(( + NestedRoute { + segments: StaticSegment("/"), + children: ( + NestedRoute { + segments: StaticSegment(""), + children: (), + data: (), + view: || (), + }, + NestedRoute { + segments: StaticSegment("about"), + children: (), + data: (), + view: || (), + }, + ), + data: (), + view: || (), + }, + NestedRoute { + segments: StaticSegment("/blog"), + children: ( + NestedRoute { + segments: StaticSegment(""), + children: (), + data: (), + view: || (), + }, + NestedRoute { + segments: (StaticSegment("post"), ParamSegment("id")), + children: (), + data: (), + view: || (), + }, + ), + data: (), + view: || (), + }, + )); + + // generates routes correctly + let (base, paths) = routes.generate_routes(); + assert_eq!(base, None); + let paths = paths.into_iter().collect::>(); + assert_eq!( + paths, + vec![ + vec![ + PathSegment::Static("/".into()), + PathSegment::Static("".into()), + ], + vec![ + PathSegment::Static("/".into()), + PathSegment::Static("about".into()) + ], + vec![ + PathSegment::Static("/blog".into()), + PathSegment::Static("".into()), + ], + vec![ + PathSegment::Static("/blog".into()), + PathSegment::Static("post".into()), + PathSegment::Param("id".into()) + ] + ] + ); + + let matched = routes.match_route("/about").unwrap(); + let params = matched.to_params().collect::>(); + assert!(params.is_empty()); + let matched = routes.match_route("/blog").unwrap(); + let params = matched.to_params().collect::>(); + assert!(params.is_empty()); + let matched = routes.match_route("/blog/post/42").unwrap(); + let params = matched.to_params().collect::>(); + assert_eq!(params, vec![("id", "42")]); + } + + #[test] + pub fn arbitrary_nested_routes() { + let routes = Routes::new_with_base( + ( + NestedRoute { + segments: StaticSegment("/"), + children: ( + NestedRoute { + segments: StaticSegment("/"), + children: (), + data: (), + view: || (), + }, + NestedRoute { + segments: StaticSegment("about"), + children: (), + data: (), + view: || (), + }, + ), + data: (), + view: || (), + }, + NestedRoute { + segments: StaticSegment("/blog"), + children: ( + NestedRoute { + segments: StaticSegment(""), + children: (), + data: (), + view: || (), + }, + NestedRoute { + segments: StaticSegment("category"), + children: (), + data: (), + view: || (), + }, + NestedRoute { + segments: ( + StaticSegment("post"), + ParamSegment("id"), + ), + children: (), + data: (), + view: || (), + }, + ), + data: (), + view: || (), + }, + NestedRoute { + segments: ( + StaticSegment("/contact"), + WildcardSegment("any"), + ), + children: (), + data: (), + view: || (), + }, + ), + "/portfolio", + ); + + // generates routes correctly + let (base, _paths) = routes.generate_routes(); + assert_eq!(base, Some("/portfolio")); + + let matched = routes.match_route("/about"); + assert!(matched.is_none()); + + let matched = routes.match_route("/portfolio/about").unwrap(); + let params = matched.to_params().collect::>(); + assert!(params.is_empty()); + + let matched = routes.match_route("/portfolio/blog/post/42").unwrap(); + let params = matched.to_params().collect::>(); + assert_eq!(params, vec![("id", "42")]); + + let matched = routes.match_route("/portfolio/contact").unwrap(); + let params = matched.to_params().collect::>(); + assert_eq!(params, vec![("any", "")]); + + let matched = routes.match_route("/portfolio/contact/foobar").unwrap(); + let params = matched.to_params().collect::>(); + assert_eq!(params, vec![("any", "foobar")]); + } +} + +#[derive(Debug)] +pub struct PartialPathMatch<'a, ParamsIter> { + pub(crate) remaining: &'a str, + pub(crate) params: ParamsIter, + pub(crate) matched: &'a str, +} + +impl<'a, ParamsIter> PartialPathMatch<'a, ParamsIter> { + pub fn new( + remaining: &'a str, + params: ParamsIter, + matched: &'a str, + ) -> Self { + Self { + remaining, + params, + matched, + } + } + + pub fn is_complete(&self) -> bool { + self.remaining.is_empty() || self.remaining == "/" + } + + pub fn remaining(&self) -> &str { + self.remaining + } + + pub fn params(self) -> ParamsIter { + self.params + } + + pub fn matched(&self) -> &str { + self.matched + } +} diff --git a/routing_utils/src/matching/mod.rs b/routing_utils/src/matching/mod.rs deleted file mode 100644 index 3b410f369..000000000 --- a/routing_utils/src/matching/mod.rs +++ /dev/null @@ -1,382 +0,0 @@ -mod horizontal; -mod nested; -mod vertical; -use crate::PathSegment; -use alloc::borrow::Cow; -pub use horizontal::*; -pub use nested::*; -pub use vertical::*; - -#[derive(Debug)] -pub struct Routes { - base: Option>, - children: Children, -} - -impl Routes { - pub fn new(children: Children) -> Self { - Self { - base: None, - children, - } - } - - pub fn new_with_base( - children: Children, - base: impl Into>, - ) -> Self { - Self { - base: Some(base.into()), - children, - } - } -} - -impl<'a, Children> Routes -where - Children: MatchNestedRoutes<'a>, -{ - pub fn match_route(&'a self, path: &'a str) -> Option { - let path = match &self.base { - None => path, - Some(base) => { - let (base, path) = if base.starts_with('/') { - (base.trim_start_matches('/'), path.trim_start_matches('/')) - } else { - (base.as_ref(), path) - }; - match path.strip_prefix(base) { - Some(path) => path, - None => return None, - } - } - }; - - let (matched, remaining) = self.children.match_nested(path); - let matched = matched?; - - if !remaining.is_empty() { - None - } else { - Some(matched) - } - } - - pub fn generate_routes( - &'a self, - ) -> ( - Option<&str>, - impl IntoIterator> + 'a, - ) { - (self.base.as_deref(), self.children.generate_routes()) - } -} - -pub trait MatchInterface<'a> { - type Params: IntoIterator; - type Child; - type View; - - fn to_params(&self) -> Self::Params; - - fn to_child(&'a self) -> Self::Child; - - fn to_view(&self) -> Self::View; -} - -pub trait MatchNestedRoutes<'a> { - type Data; - type Match: MatchInterface<'a>; - - fn match_nested(&'a self, path: &'a str) -> (Option, &'a str); - - fn generate_routes( - &self, - ) -> impl IntoIterator> + '_; -} - -#[cfg(test)] -mod tests { - use super::{NestedRoute, ParamSegment, Routes}; - use crate::{ - matching::{MatchInterface, StaticSegment, WildcardSegment}, - PathSegment, - }; - - #[test] - pub fn matches_single_root_route() { - let routes = Routes::new(NestedRoute { - segments: StaticSegment("/"), - children: (), - data: (), - view: || (), - }); - let matched = routes.match_route("/"); - assert!(matched.is_some()); - let matched = routes.match_route(""); - assert!(matched.is_some()); - let (base, paths) = routes.generate_routes(); - assert_eq!(base, None); - let paths = paths.into_iter().collect::>(); - assert_eq!(paths, vec![vec![PathSegment::Static("/".into())]]); - } - - #[test] - pub fn matches_nested_route() { - let routes = Routes::new(NestedRoute { - segments: StaticSegment(""), - children: NestedRoute { - segments: (StaticSegment("author"), StaticSegment("contact")), - children: (), - data: (), - view: "Contact Me", - }, - data: (), - view: "Home", - }); - - // route generation - let (base, paths) = routes.generate_routes(); - assert_eq!(base, None); - let paths = paths.into_iter().collect::>(); - assert_eq!( - paths, - vec![vec![ - PathSegment::Static("".into()), - PathSegment::Static("author".into()), - PathSegment::Static("contact".into()) - ]] - ); - - let matched = routes.match_route("/author/contact").unwrap(); - assert_eq!(matched.matched(), ""); - assert_eq!(matched.to_child().matched(), "/author/contact"); - - let view = matched.to_view(); - assert_eq!(*view, "Home"); - assert_eq!(*matched.to_child().to_view(), "Contact Me"); - } - - #[test] - pub fn does_not_match_incomplete_route() { - let routes = Routes::new(NestedRoute { - segments: StaticSegment(""), - children: NestedRoute { - segments: (StaticSegment("author"), StaticSegment("contact")), - children: (), - data: (), - view: "Contact Me", - }, - data: (), - view: "Home", - }); - let matched = routes.match_route("/"); - assert!(matched.is_none()); - } - - #[test] - pub fn chooses_between_nested_routes() { - let routes = Routes::new(( - NestedRoute { - segments: StaticSegment("/"), - children: ( - NestedRoute { - segments: StaticSegment(""), - children: (), - data: (), - view: || (), - }, - NestedRoute { - segments: StaticSegment("about"), - children: (), - data: (), - view: || (), - }, - ), - data: (), - view: || (), - }, - NestedRoute { - segments: StaticSegment("/blog"), - children: ( - NestedRoute { - segments: StaticSegment(""), - children: (), - data: (), - view: || (), - }, - NestedRoute { - segments: (StaticSegment("post"), ParamSegment("id")), - children: (), - data: (), - view: || (), - }, - ), - data: (), - view: || (), - }, - )); - - // generates routes correctly - let (base, paths) = routes.generate_routes(); - assert_eq!(base, None); - let paths = paths.into_iter().collect::>(); - assert_eq!( - paths, - vec![ - vec![ - PathSegment::Static("/".into()), - PathSegment::Static("".into()), - ], - vec![ - PathSegment::Static("/".into()), - PathSegment::Static("about".into()) - ], - vec![ - PathSegment::Static("/blog".into()), - PathSegment::Static("".into()), - ], - vec![ - PathSegment::Static("/blog".into()), - PathSegment::Static("post".into()), - PathSegment::Param("id".into()) - ] - ] - ); - - let matched = routes.match_route("/about").unwrap(); - let params = matched.to_params().collect::>(); - assert!(params.is_empty()); - let matched = routes.match_route("/blog").unwrap(); - let params = matched.to_params().collect::>(); - assert!(params.is_empty()); - let matched = routes.match_route("/blog/post/42").unwrap(); - let params = matched.to_params().collect::>(); - assert_eq!(params, vec![("id", "42")]); - } - - #[test] - pub fn arbitrary_nested_routes() { - let routes = Routes::new_with_base( - ( - NestedRoute { - segments: StaticSegment("/"), - children: ( - NestedRoute { - segments: StaticSegment("/"), - children: (), - data: (), - view: || (), - }, - NestedRoute { - segments: StaticSegment("about"), - children: (), - data: (), - view: || (), - }, - ), - data: (), - view: || (), - }, - NestedRoute { - segments: StaticSegment("/blog"), - children: ( - NestedRoute { - segments: StaticSegment(""), - children: (), - data: (), - view: || (), - }, - NestedRoute { - segments: StaticSegment("category"), - children: (), - data: (), - view: || (), - }, - NestedRoute { - segments: ( - StaticSegment("post"), - ParamSegment("id"), - ), - children: (), - data: (), - view: || (), - }, - ), - data: (), - view: || (), - }, - NestedRoute { - segments: ( - StaticSegment("/contact"), - WildcardSegment("any"), - ), - children: (), - data: (), - view: || (), - }, - ), - "/portfolio", - ); - - // generates routes correctly - let (base, _paths) = routes.generate_routes(); - assert_eq!(base, Some("/portfolio")); - - let matched = routes.match_route("/about"); - assert!(matched.is_none()); - - let matched = routes.match_route("/portfolio/about").unwrap(); - let params = matched.to_params().collect::>(); - assert!(params.is_empty()); - - let matched = routes.match_route("/portfolio/blog/post/42").unwrap(); - let params = matched.to_params().collect::>(); - assert_eq!(params, vec![("id", "42")]); - - let matched = routes.match_route("/portfolio/contact").unwrap(); - let params = matched.to_params().collect::>(); - assert_eq!(params, vec![("any", "")]); - - let matched = routes.match_route("/portfolio/contact/foobar").unwrap(); - let params = matched.to_params().collect::>(); - assert_eq!(params, vec![("any", "foobar")]); - } -} - -#[derive(Debug)] -pub struct PartialPathMatch<'a, ParamsIter> { - pub(crate) remaining: &'a str, - pub(crate) params: ParamsIter, - pub(crate) matched: &'a str, -} - -impl<'a, ParamsIter> PartialPathMatch<'a, ParamsIter> { - pub fn new( - remaining: &'a str, - params: ParamsIter, - matched: &'a str, - ) -> Self { - Self { - remaining, - params, - matched, - } - } - - pub fn is_complete(&self) -> bool { - self.remaining.is_empty() || self.remaining == "/" - } - - pub fn remaining(&self) -> &str { - self.remaining - } - - pub fn params(self) -> ParamsIter { - self.params - } - - pub fn matched(&self) -> &str { - self.matched - } -} diff --git a/routing_utils/src/matching/nested/mod.rs b/routing_utils/src/nested/mod.rs similarity index 88% rename from routing_utils/src/matching/nested/mod.rs rename to routing_utils/src/nested/mod.rs index 95922221f..7e335b2d3 100644 --- a/routing_utils/src/matching/nested/mod.rs +++ b/routing_utils/src/nested/mod.rs @@ -2,7 +2,8 @@ use super::{ MatchInterface, MatchNestedRoutes, PartialPathMatch, PossibleRouteMatch, }; use crate::PathSegment; -use core::iter; +use alloc::vec::Vec; +use core::{fmt, iter}; mod tuples; @@ -14,7 +15,7 @@ pub struct NestedRoute { pub view: View, } -#[derive(Debug, PartialEq, Eq)] +#[derive(PartialEq, Eq)] pub struct NestedMatch<'a, ParamsIter, Child, View> { /// The portion of the full path matched only by this nested route. matched: &'a str, @@ -25,6 +26,21 @@ pub struct NestedMatch<'a, ParamsIter, Child, View> { view: &'a View, } +impl<'a, ParamsIter, Child, View> fmt::Debug + for NestedMatch<'a, ParamsIter, Child, View> +where + ParamsIter: fmt::Debug, + Child: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("NestedMatch") + .field("matched", &self.matched) + .field("params", &self.params) + .field("child", &self.child) + .finish() + } +} + impl<'a, ParamsIter, Child, View> MatchInterface<'a> for NestedMatch<'a, ParamsIter, Child, View> where diff --git a/routing_utils/src/matching/nested/tuples.rs b/routing_utils/src/nested/tuples.rs similarity index 98% rename from routing_utils/src/matching/nested/tuples.rs rename to routing_utils/src/nested/tuples.rs index 9ab7285f6..6cdb29992 100644 --- a/routing_utils/src/matching/nested/tuples.rs +++ b/routing_utils/src/nested/tuples.rs @@ -1,7 +1,5 @@ -use crate::{ - matching::{MatchInterface, MatchNestedRoutes}, - PathSegment, -}; +use crate::{MatchInterface, MatchNestedRoutes, PathSegment}; +use alloc::vec::Vec; use core::iter; use either_of::*; diff --git a/routing_utils/src/params.rs b/routing_utils/src/params.rs deleted file mode 100644 index e4183e606..000000000 --- a/routing_utils/src/params.rs +++ /dev/null @@ -1,4 +0,0 @@ -extern crate alloc; -use alloc::{string::String, vec::Vec}; - -pub(crate) type Params = Vec<(K, String)>; diff --git a/routing_utils/src/matching/vertical/mod.rs b/routing_utils/src/vertical/mod.rs similarity index 100% rename from routing_utils/src/matching/vertical/mod.rs rename to routing_utils/src/vertical/mod.rs