1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
use crate::tree::Node;
use crate::{InsertError, MatchError, Params};
/// A URL router.
///
/// See [the crate documentation](crate) for details.
#[derive(Clone)]
pub struct Router<T> {
root: Node<T>,
}
impl<T> Default for Router<T> {
fn default() -> Self {
Self {
root: Node::default(),
}
}
}
impl<T> Router<T> {
/// Construct a new router.
pub fn new() -> Self {
Self::default()
}
/// Insert a route.
///
/// # Examples
///
/// ```rust
/// # 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")?;
/// # Ok(())
/// # }
/// ```
pub fn insert(&mut self, route: impl Into<String>, value: T) -> Result<(), InsertError> {
self.root.insert(route, value)
}
/// Tries to find a value in the router matching the given path.
///
/// # Examples
///
/// ```rust
/// # use matchit::Router;
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let mut router = Router::new();
/// router.insert("/home", "Welcome!")?;
///
/// let matched = router.at("/home").unwrap();
/// assert_eq!(*matched.value, "Welcome!");
/// # Ok(())
/// # }
/// ```
pub fn at<'m, 'p>(&'m self, path: &'p str) -> Result<Match<'m, 'p, &'m T>, MatchError> {
match self.root.at(path.as_bytes()) {
Ok((value, params)) => Ok(Match {
// SAFETY: We only expose &mut T through &mut self
value: unsafe { &*value.get() },
params,
}),
Err(e) => Err(e),
}
}
/// Tries to find a value in the router matching the given path,
/// returning a mutable reference.
///
/// # Examples
///
/// ```rust
/// # use matchit::Router;
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let mut router = Router::new();
/// router.insert("/", 1)?;
///
/// *router.at_mut("/").unwrap().value += 1;
/// assert_eq!(*router.at("/").unwrap().value, 2);
/// # Ok(())
/// # }
/// ```
pub fn at_mut<'m, 'p>(
&'m mut self,
path: &'p str,
) -> Result<Match<'m, 'p, &'m mut T>, MatchError> {
match self.root.at(path.as_bytes()) {
Ok((value, params)) => Ok(Match {
// SAFETY: We have &mut self
value: unsafe { &mut *value.get() },
params,
}),
Err(e) => Err(e),
}
}
/// Performs a case-insensitive lookup of the given path,
/// returning the case corrected path if successful.
///
/// Missing/extra trailing slashes are also corrected.
///
/// ```rust
/// # use matchit::Router;
///
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let mut router = Router::new();
/// router.insert("/home", "Welcome!")?;
///
/// let path = router.fix_path("/HoMe/").unwrap();
/// assert_eq!(path, "/home");
/// # Ok(())
/// # }
/// ````
pub fn fix_path(&self, path: &str) -> Option<String> {
self.root.fix_path(path)
}
#[cfg(feature = "__test_helpers")]
pub fn check_priorities(&self) -> Result<u32, (u32, u32)> {
self.root.check_priorities()
}
}
/// A successful match consisting of the registered value
/// and URL parameters, returned by [`Router::at`](Router::at).
#[derive(Debug)]
pub struct Match<'k, 'v, V> {
/// The value stored under the matched node.
pub value: V,
/// The route parameters. See [parameters](crate#parameters) for more details.
pub params: Params<'k, 'v>,
}