matchit/
router.rs

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