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}