feat: allow using enums for StaticSegment by implementing AsPath (#2685)

This commit is contained in:
mahmoud-eltahawy 2024-07-19 01:20:00 +03:00 committed by Greg Johnston
parent a2385e4c42
commit 873aec5787

View file

@ -1,6 +1,6 @@
use super::{PartialPathMatch, PathSegment, PossibleRouteMatch}; use super::{PartialPathMatch, PathSegment, PossibleRouteMatch};
use core::iter; use core::iter;
use std::borrow::Cow; use std::{borrow::Cow, fmt::Debug};
impl PossibleRouteMatch for () { impl PossibleRouteMatch for () {
type ParamsIter = iter::Empty<(Cow<'static, str>, String)>; type ParamsIter = iter::Empty<(Cow<'static, str>, String)>;
@ -15,10 +15,20 @@ impl PossibleRouteMatch for () {
fn generate_path(&self, _path: &mut Vec<PathSegment>) {} fn generate_path(&self, _path: &mut Vec<PathSegment>) {}
} }
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub trait AsPath {
pub struct StaticSegment(pub &'static str); fn as_path(&self) -> &'static str;
}
impl PossibleRouteMatch for StaticSegment { impl AsPath for &'static str {
fn as_path(&self) -> &'static str {
self
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct StaticSegment<T: AsPath>(pub T);
impl<T: AsPath> PossibleRouteMatch for StaticSegment<T> {
type ParamsIter = iter::Empty<(Cow<'static, str>, String)>; type ParamsIter = iter::Empty<(Cow<'static, str>, String)>;
fn test<'a>( fn test<'a>(
@ -27,17 +37,19 @@ impl PossibleRouteMatch for StaticSegment {
) -> Option<PartialPathMatch<'a, Self::ParamsIter>> { ) -> Option<PartialPathMatch<'a, Self::ParamsIter>> {
let mut matched_len = 0; let mut matched_len = 0;
let mut test = path.chars().peekable(); let mut test = path.chars().peekable();
let mut this = self.0.chars(); let mut this = self.0.as_path().chars();
let mut has_matched = self.0.is_empty() || self.0 == "/"; let mut has_matched =
self.0.as_path().is_empty() || self.0.as_path() == "/";
// match an initial / // match an initial /
if let Some('/') = test.peek() { if let Some('/') = test.peek() {
test.next(); test.next();
if !self.0.is_empty() { if !self.0.as_path().is_empty() {
matched_len += 1; matched_len += 1;
} }
if self.0.starts_with('/') || self.0.is_empty() { if self.0.as_path().starts_with('/') || self.0.as_path().is_empty()
{
this.next(); this.next();
} }
} }
@ -70,14 +82,33 @@ impl PossibleRouteMatch for StaticSegment {
} }
fn generate_path(&self, path: &mut Vec<PathSegment>) { fn generate_path(&self, path: &mut Vec<PathSegment>) {
path.push(PathSegment::Static(self.0.into())) path.push(PathSegment::Static(self.0.as_path().into()))
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::AsPath;
use super::{PossibleRouteMatch, StaticSegment}; use super::{PossibleRouteMatch, StaticSegment};
#[derive(Debug, Clone)]
enum Paths {
Foo,
Bar,
}
impl AsPath for Paths {
fn as_path(&self) -> &'static str {
match self {
Foo => "foo",
Bar => "bar",
}
}
}
use Paths::*;
#[test] #[test]
fn single_static_match() { fn single_static_match() {
let path = "/foo"; let path = "/foo";
@ -89,6 +120,17 @@ mod tests {
assert!(params.is_empty()); assert!(params.is_empty());
} }
#[test]
fn single_static_match_on_enum() {
let path = "/foo";
let def = StaticSegment(Foo);
let matched = def.test(path).expect("couldn't match route");
assert_eq!(matched.matched(), "/foo");
assert_eq!(matched.remaining(), "");
let params = matched.params().collect::<Vec<_>>();
assert!(params.is_empty());
}
#[test] #[test]
fn single_static_mismatch() { fn single_static_mismatch() {
let path = "/foo"; let path = "/foo";
@ -96,6 +138,13 @@ mod tests {
assert!(def.test(path).is_none()); assert!(def.test(path).is_none());
} }
#[test]
fn single_static_mismatch_on_enum() {
let path = "/foo";
let def = StaticSegment(Bar);
assert!(def.test(path).is_none());
}
#[test] #[test]
fn single_static_match_with_trailing_slash() { fn single_static_match_with_trailing_slash() {
let path = "/foo/"; let path = "/foo/";
@ -107,6 +156,17 @@ mod tests {
assert!(params.is_empty()); assert!(params.is_empty());
} }
#[test]
fn single_static_match_with_trailing_slash_on_enum() {
let path = "/foo/";
let def = StaticSegment(Foo);
let matched = def.test(path).expect("couldn't match route");
assert_eq!(matched.matched(), "/foo");
assert_eq!(matched.remaining(), "/");
let params = matched.params().collect::<Vec<_>>();
assert!(params.is_empty());
}
#[test] #[test]
fn tuple_of_static_matches() { fn tuple_of_static_matches() {
let path = "/foo/bar"; let path = "/foo/bar";
@ -118,6 +178,17 @@ mod tests {
assert!(params.is_empty()); assert!(params.is_empty());
} }
#[test]
fn tuple_of_static_matches_on_enum() {
let path = "/foo/bar";
let def = (StaticSegment(Foo), StaticSegment(Bar));
let matched = def.test(path).expect("couldn't match route");
assert_eq!(matched.matched(), "/foo/bar");
assert_eq!(matched.remaining(), "");
let params = matched.params().collect::<Vec<_>>();
assert!(params.is_empty());
}
#[test] #[test]
fn tuple_static_mismatch() { fn tuple_static_mismatch() {
let path = "/foo/baz"; let path = "/foo/baz";
@ -125,6 +196,13 @@ mod tests {
assert!(def.test(path).is_none()); assert!(def.test(path).is_none());
} }
#[test]
fn tuple_static_mismatch_on_enum() {
let path = "/foo/baz";
let def = (StaticSegment(Foo), StaticSegment(Bar));
assert!(def.test(path).is_none());
}
#[test] #[test]
fn arbitrary_nesting_of_tuples_has_no_effect_on_matching() { fn arbitrary_nesting_of_tuples_has_no_effect_on_matching() {
let path = "/foo/bar"; let path = "/foo/bar";
@ -142,4 +220,22 @@ mod tests {
let params = matched.params().collect::<Vec<_>>(); let params = matched.params().collect::<Vec<_>>();
assert!(params.is_empty()); assert!(params.is_empty());
} }
#[test]
fn arbitrary_nesting_of_tuples_has_no_effect_on_matching_on_enum() {
let path = "/foo/bar";
let def = (
(),
(StaticSegment(Foo)),
(),
((), ()),
StaticSegment(Bar),
(),
);
let matched = def.test(path).expect("couldn't match route");
assert_eq!(matched.matched(), "/foo/bar");
assert_eq!(matched.remaining(), "");
let params = matched.params().collect::<Vec<_>>();
assert!(params.is_empty());
}
} }