matchit/
router.rs

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