From 86742ef138ff304b8106e78e70ff5a22b3b310b6 Mon Sep 17 00:00:00 2001 From: Greg Johnston Date: Sun, 29 Dec 2024 16:46:16 -0500 Subject: [PATCH 1/3] feat(breaking): allow make `PossibleRouteMatch` dyn-safe --- router/src/matching/horizontal/mod.rs | 2 +- .../src/matching/horizontal/param_segments.rs | 12 ++++++++- .../src/matching/horizontal/static_segment.rs | 8 ++++++ router/src/matching/horizontal/tuples.rs | 26 +++++++++++++------ 4 files changed, 38 insertions(+), 10 deletions(-) diff --git a/router/src/matching/horizontal/mod.rs b/router/src/matching/horizontal/mod.rs index 8bef7dcc8b..a5e99068cd 100644 --- a/router/src/matching/horizontal/mod.rs +++ b/router/src/matching/horizontal/mod.rs @@ -11,7 +11,7 @@ pub use static_segment::*; /// This is a "horizontal" matching: i.e., it treats a tuple of route segments /// as subsequent segments of the URL and tries to match them all. pub trait PossibleRouteMatch { - const OPTIONAL: bool = false; + fn optional(&self) -> bool; fn test<'a>(&self, path: &'a str) -> Option>; diff --git a/router/src/matching/horizontal/param_segments.rs b/router/src/matching/horizontal/param_segments.rs index 35ead7c048..ddef8f02cc 100644 --- a/router/src/matching/horizontal/param_segments.rs +++ b/router/src/matching/horizontal/param_segments.rs @@ -35,6 +35,10 @@ use std::borrow::Cow; pub struct ParamSegment(pub &'static str); impl PossibleRouteMatch for ParamSegment { + fn optional(&self) -> bool { + false + } + fn test<'a>(&self, path: &'a str) -> Option> { let mut matched_len = 0; let mut param_offset = 0; @@ -121,6 +125,10 @@ impl PossibleRouteMatch for ParamSegment { pub struct WildcardSegment(pub &'static str); impl PossibleRouteMatch for WildcardSegment { + fn optional(&self) -> bool { + false + } + fn test<'a>(&self, path: &'a str) -> Option> { let mut matched_len = 0; let mut param_offset = 0; @@ -158,7 +166,9 @@ impl PossibleRouteMatch for WildcardSegment { pub struct OptionalParamSegment(pub &'static str); impl PossibleRouteMatch for OptionalParamSegment { - const OPTIONAL: bool = true; + fn optional(&self) -> bool { + true + } fn test<'a>(&self, path: &'a str) -> Option> { let mut matched_len = 0; diff --git a/router/src/matching/horizontal/static_segment.rs b/router/src/matching/horizontal/static_segment.rs index 5179efecfd..d0f9ed667c 100644 --- a/router/src/matching/horizontal/static_segment.rs +++ b/router/src/matching/horizontal/static_segment.rs @@ -2,6 +2,10 @@ use super::{PartialPathMatch, PathSegment, PossibleRouteMatch}; use std::fmt::Debug; impl PossibleRouteMatch for () { + fn optional(&self) -> bool { + false + } + fn test<'a>(&self, path: &'a str) -> Option> { Some(PartialPathMatch::new(path, vec![], "")) } @@ -54,6 +58,10 @@ impl AsPath for &'static str { pub struct StaticSegment(pub T); impl PossibleRouteMatch for StaticSegment { + fn optional(&self) -> bool { + false + } + fn test<'a>(&self, path: &'a str) -> Option> { let mut matched_len = 0; let mut test = path.chars().peekable(); diff --git a/router/src/matching/horizontal/tuples.rs b/router/src/matching/horizontal/tuples.rs index a4879fb1f0..a2ec7668bb 100644 --- a/router/src/matching/horizontal/tuples.rs +++ b/router/src/matching/horizontal/tuples.rs @@ -8,15 +8,21 @@ macro_rules! tuples { $first: PossibleRouteMatch, $($ty: PossibleRouteMatch),*, { + fn optional(&self) -> bool { + #[allow(non_snake_case)] + let ($first, $($ty,)*) = &self; + [$first.optional(), $($ty.optional()),*].into_iter().any(|n| n) + } + fn test<'a>(&self, path: &'a str) -> Option> { + #[allow(non_snake_case)] + let ($first, $($ty,)*) = &self; + // on the first run, include all optionals let mut include_optionals = { - [$first::OPTIONAL, $($ty::OPTIONAL),*].into_iter().filter(|n| *n).count() + [$first.optional(), $($ty.optional()),*].into_iter().filter(|n| *n).count() }; - #[allow(non_snake_case)] - let ($first, $($ty,)*) = &self; - loop { let mut nth_field = 0; let mut matched_len = 0; @@ -25,7 +31,7 @@ macro_rules! tuples { let mut p = Vec::new(); let mut m = String::new(); - if !$first::OPTIONAL || nth_field < include_optionals { + if !$first.optional() || nth_field < include_optionals { match $first.test(r) { None => { return None; @@ -40,16 +46,16 @@ macro_rules! tuples { matched_len += m.len(); $( - if $ty::OPTIONAL { + if $ty.optional() { nth_field += 1; } - if !$ty::OPTIONAL || nth_field < include_optionals { + if !$ty.optional() || nth_field < include_optionals { let PartialPathMatch { remaining, matched, params } = match $ty.test(r) { - None => if $ty::OPTIONAL { + None => if $ty.optional() { return None; } else { if include_optionals == 0 { @@ -90,6 +96,10 @@ where Self: core::fmt::Debug, A: PossibleRouteMatch, { + fn optional(&self) -> bool { + self.0.optional() + } + fn test<'a>(&self, path: &'a str) -> Option> { let remaining = path; let PartialPathMatch { From 0a4ea72f0173119745bfb9ae01dff8076eb3df89 Mon Sep 17 00:00:00 2001 From: Greg Johnston Date: Sun, 29 Dec 2024 16:49:32 -0500 Subject: [PATCH 2/3] feat(breaking): allow make `PossibleRouteMatch` dyn-safe --- router/src/matching/horizontal/mod.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/router/src/matching/horizontal/mod.rs b/router/src/matching/horizontal/mod.rs index a5e99068cd..bbdc13d96b 100644 --- a/router/src/matching/horizontal/mod.rs +++ b/router/src/matching/horizontal/mod.rs @@ -17,3 +17,17 @@ pub trait PossibleRouteMatch { fn generate_path(&self, path: &mut Vec); } + +impl PossibleRouteMatch for Box { + fn optional(&self) -> bool { + (**self).optional() + } + + fn test<'a>(&self, path: &'a str) -> Option> { + (**self).test(path) + } + + fn generate_path(&self, path: &mut Vec) { + (**self).generate_path(path); + } +} From 7bc25a01a5249d5691c51ed285182ffc9d116bd6 Mon Sep 17 00:00:00 2001 From: Greg Johnston Date: Sun, 29 Dec 2024 16:52:38 -0500 Subject: [PATCH 3/3] feat(breaking): allow make `PossibleRouteMatch` dyn-safe --- router/src/matching/horizontal/mod.rs | 17 ++++++++++++++++- router/src/matching/nested/mod.rs | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/router/src/matching/horizontal/mod.rs b/router/src/matching/horizontal/mod.rs index bbdc13d96b..a8846831a2 100644 --- a/router/src/matching/horizontal/mod.rs +++ b/router/src/matching/horizontal/mod.rs @@ -1,4 +1,5 @@ use super::{PartialPathMatch, PathSegment}; +use std::sync::Arc; mod param_segments; mod static_segment; mod tuples; @@ -18,7 +19,21 @@ pub trait PossibleRouteMatch { fn generate_path(&self, path: &mut Vec); } -impl PossibleRouteMatch for Box { +impl PossibleRouteMatch for Box { + fn optional(&self) -> bool { + (**self).optional() + } + + fn test<'a>(&self, path: &'a str) -> Option> { + (**self).test(path) + } + + fn generate_path(&self, path: &mut Vec) { + (**self).generate_path(path); + } +} + +impl PossibleRouteMatch for Arc { fn optional(&self) -> bool { (**self).optional() } diff --git a/router/src/matching/nested/mod.rs b/router/src/matching/nested/mod.rs index ea0b21dfa1..67ab2826e5 100644 --- a/router/src/matching/nested/mod.rs +++ b/router/src/matching/nested/mod.rs @@ -151,7 +151,7 @@ impl MatchNestedRoutes for NestedRoute where Self: 'static, - Segments: PossibleRouteMatch + std::fmt::Debug, + Segments: PossibleRouteMatch, Children: MatchNestedRoutes, Children::Match: MatchParams, Children: 'static,