Skip to content

Commit

Permalink
new route syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
ibraheemdev committed Feb 27, 2024
1 parent f7cd626 commit a9d6be8
Show file tree
Hide file tree
Showing 6 changed files with 406 additions and 388 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use matchit::Router;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut router = Router::new();
router.insert("/home", "Welcome!")?;
router.insert("/users/:id", "A User")?;
router.insert("/users/{id}", "A User")?;

let matched = router.at("/users/978")?;
assert_eq!(matched.params.get("id"), Some("978"));
Expand All @@ -28,11 +28,11 @@ Along with static routes, the router also supports dynamic route segments. These

### Named Parameters

Named parameters like `/:id` match anything until the next `/` or the end of the path:
Named parameters like `/{id}` match anything until the next `/` or the end of the path:

```rust,ignore
let mut m = Router::new();
m.insert("/users/:id", true)?;
m.insert("/users/{id}", true)?;
assert_eq!(m.at("/users/1")?.params.get("id"), Some("1"));
assert_eq!(m.at("/users/23")?.params.get("id"), Some("23"));
Expand All @@ -45,7 +45,7 @@ Catch-all parameters start with `*` and match anything until the end of the path

```rust,ignore
let mut m = Router::new();
m.insert("/*p", true)?;
m.insert("/{*p}", true)?;
assert_eq!(m.at("/foo.js")?.params.get("p"), Some("foo.js"));
assert_eq!(m.at("/c/bar.css")?.params.get("p"), Some("c/bar.css"));
Expand All @@ -62,7 +62,7 @@ Static and dynamic route segments are allowed to overlap. If they do, static seg
let mut m = Router::new();
m.insert("/", "Welcome!").unwrap(); // priority: 1
m.insert("/about", "About Me").unwrap(); // priority: 1
m.insert("/*filepath", "...").unwrap(); // priority: 2
m.insert("/{*filepath}", "...").unwrap(); // priority: 2
```

## How does it work?
Expand Down
10 changes: 5 additions & 5 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ pub enum InsertError {
with: String,
},
/// Only one parameter per route segment is allowed.
TooManyParams,
/// Parameters must be registered with a name.
UnnamedParam,
InvalidParam,
/// Parameters must be registered with a valid name.
InvalidParamName,
/// Catch-all parameters are only allowed at the end of a path.
InvalidCatchAll,
}
Expand All @@ -29,8 +29,8 @@ impl fmt::Display for InsertError {
with
)
}
Self::TooManyParams => write!(f, "only one parameter is allowed per path segment"),
Self::UnnamedParam => write!(f, "parameters must be registered with a name"),
Self::InvalidParam => write!(f, "only one parameter is allowed per path segment"),
Self::InvalidParamName => write!(f, "parameters must be registered with a valid name"),
Self::InvalidCatchAll => write!(
f,
"catch-all parameters are only allowed at the end of a route"
Expand Down
10 changes: 5 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let mut router = Router::new();
//! router.insert("/home", "Welcome!")?;
//! router.insert("/users/:id", "A User")?;
//! router.insert("/users/{id}", "A User")?;
//!
//! let matched = router.at("/users/978")?;
//! assert_eq!(matched.params.get("id"), Some("978"));
Expand All @@ -27,13 +27,13 @@
//!
//! ### Named Parameters
//!
//! Named parameters like `/:id` match anything until the next `/` or the end of the path:
//! Named parameters like `/{id}` match anything until the next `/` or the end of the path:
//!
//! ```rust
//! # use matchit::Router;
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let mut m = Router::new();
//! m.insert("/users/:id", true)?;
//! m.insert("/users/{id}", true)?;
//!
//! assert_eq!(m.at("/users/1")?.params.get("id"), Some("1"));
//! assert_eq!(m.at("/users/23")?.params.get("id"), Some("23"));
Expand All @@ -52,7 +52,7 @@
//! # use matchit::Router;
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let mut m = Router::new();
//! m.insert("/*p", true)?;
//! m.insert("/{*p}", true)?;
//!
//! assert_eq!(m.at("/foo.js")?.params.get("p"), Some("foo.js"));
//! assert_eq!(m.at("/c/bar.css")?.params.get("p"), Some("c/bar.css"));
Expand All @@ -74,7 +74,7 @@
//! let mut m = Router::new();
//! m.insert("/", "Welcome!").unwrap() ; // priority: 1
//! m.insert("/about", "About Me").unwrap(); // priority: 1
//! m.insert("/*filepath", "...").unwrap(); // priority: 2
//! m.insert("/{*filepath}", "...").unwrap(); // priority: 2
//!
//! # Ok(())
//! # }
Expand Down
28 changes: 14 additions & 14 deletions src/params.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
use std::iter;
use std::mem;
use std::slice;
use std::{fmt, iter, mem, slice};

/// A single URL parameter, consisting of a key and a value.
#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Default, Copy, Clone)]
#[derive(PartialEq, Eq, Ord, PartialOrd, Default, Copy, Clone)]
struct Param<'k, 'v> {
key: &'k [u8],
value: &'v [u8],
Expand All @@ -25,7 +23,7 @@ impl<'k, 'v> Param<'k, 'v> {
/// ```rust
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// # let mut router = matchit::Router::new();
/// # router.insert("/users/:id", true).unwrap();
/// # router.insert("/users/{id}", true).unwrap();
/// let matched = router.at("/users/1")?;
///
/// // you can iterate through the keys and values
Expand All @@ -39,15 +37,15 @@ impl<'k, 'v> Param<'k, 'v> {
/// # Ok(())
/// # }
/// ```
#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Clone)]
#[derive(PartialEq, Eq, Ord, PartialOrd, Clone)]
pub struct Params<'k, 'v> {
kind: ParamsKind<'k, 'v>,
}

// most routes have 1-3 dynamic parameters, so we can avoid a heap allocation in common cases.
const SMALL: usize = 3;

#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Clone)]
#[derive(PartialEq, Eq, Ord, PartialOrd, Clone)]
enum ParamsKind<'k, 'v> {
None,
Small([Param<'k, 'v>; SMALL], usize),
Expand All @@ -56,8 +54,9 @@ enum ParamsKind<'k, 'v> {

impl<'k, 'v> Params<'k, 'v> {
pub(crate) fn new() -> Self {

Check warning on line 56 in src/params.rs

View workflow job for this annotation

GitHub Actions / Clippy Lints

this could be a `const fn`

Check warning on line 56 in src/params.rs

View workflow job for this annotation

GitHub Actions / Clippy Lints

this could be a `const fn`
let kind = ParamsKind::None;
Self { kind }
Self {
kind: ParamsKind::None,
}
}

/// Returns the number of parameters.
Expand Down Expand Up @@ -159,6 +158,12 @@ impl<'k, 'v> Params<'k, 'v> {
}
}

impl fmt::Debug for Params<'_, '_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.iter()).finish()
}
}

/// An iterator over the keys and values of a route's [parameters](crate::Params).
pub struct ParamsIter<'ps, 'k, 'v> {
kind: ParamsIterKind<'ps, 'k, 'v>,
Expand Down Expand Up @@ -201,11 +206,6 @@ impl<'ps, 'k, 'v> Iterator for ParamsIter<'ps, 'k, 'v> {
mod tests {
use super::*;

#[test]
fn no_alloc() {
assert_eq!(Params::new().kind, ParamsKind::None);
}

#[test]
fn heap_alloc() {
let vec = vec![
Expand Down
Loading

0 comments on commit a9d6be8

Please sign in to comment.