xitca_router/router.rs
1use crate::error::MergeError;
2use crate::tree::Node;
3use crate::{params::Params, InsertError, MatchError};
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 { root: Node::default() }
16 }
17}
18
19impl<T> Router<T> {
20 /// Construct a new router.
21 pub fn new() -> Self {
22 Self::default()
23 }
24
25 /// Insert a route into the router.
26 ///
27 /// # Examples
28 ///
29 /// ```rust
30 /// # use xitca_router::Router;
31 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
32 /// let mut router = Router::new();
33 /// router.insert("/home", "Welcome!")?;
34 /// router.insert("/users/{id}", "A User")?;
35 /// # Ok(())
36 /// # }
37 /// ```
38 pub fn insert(&mut self, route: impl Into<String>, value: T) -> Result<(), InsertError> {
39 self.root.insert(route.into(), value)
40 }
41
42 /// Tries to find a value in the router matching the given path.
43 ///
44 /// # Examples
45 ///
46 /// ```rust
47 /// # use xitca_router::Router;
48 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
49 /// let mut router = Router::new();
50 /// router.insert("/home", "Welcome!")?;
51 ///
52 /// let matched = router.at("/home").unwrap();
53 /// assert_eq!(*matched.value, "Welcome!");
54 /// # Ok(())
55 /// # }
56 /// ```
57 #[inline]
58 pub fn at(&self, path: &str) -> Result<Match<&T>, MatchError> {
59 self.root.at(path).map(|(value, params)| Match { value, params })
60 }
61
62 /// Remove a given route from the router.
63 ///
64 /// Returns the value stored under the route if it was found.
65 /// If the route was not found or invalid, `None` is returned.
66 ///
67 /// # Examples
68 ///
69 /// ```rust
70 /// # use xitca_router::Router;
71 /// let mut router = Router::new();
72 ///
73 /// router.insert("/home", "Welcome!");
74 /// assert_eq!(router.remove("/home"), Some("Welcome!"));
75 /// assert_eq!(router.remove("/home"), None);
76 ///
77 /// router.insert("/home/{id}/", "Hello!");
78 /// assert_eq!(router.remove("/home/{id}/"), Some("Hello!"));
79 /// assert_eq!(router.remove("/home/{id}/"), None);
80 ///
81 /// router.insert("/home/{id}/", "Hello!");
82 /// // The route does not match.
83 /// assert_eq!(router.remove("/home/{user}"), None);
84 /// assert_eq!(router.remove("/home/{id}/"), Some("Hello!"));
85 ///
86 /// router.insert("/home/{id}/", "Hello!");
87 /// // Invalid route.
88 /// assert_eq!(router.remove("/home/{id}"), None);
89 /// assert_eq!(router.remove("/home/{id}/"), Some("Hello!"));
90 /// ```
91 pub fn remove(&mut self, path: impl Into<String>) -> Option<T> {
92 self.root.remove(path.into())
93 }
94
95 #[doc(hidden)]
96 /// Test helper that ensures route priorities are consistent.
97 pub fn check_priorities(&self) -> Result<u32, (u32, u32)> {
98 self.root.check_priorities()
99 }
100
101 /// Merge a given router into current one.
102 ///
103 /// Returns a list of [`InsertError`] for every failed insertion.
104 /// Note that this can result in a partially successful merge if
105 /// a subset of routes conflict.
106 ///
107 /// # Examples
108 ///
109 /// ```rust
110 /// # use xitca_router::Router;
111 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
112 /// let mut root = Router::new();
113 /// root.insert("/home", "Welcome!")?;
114 ///
115 /// let mut child = Router::new();
116 /// child.insert("/users/{id}", "A User")?;
117 ///
118 /// root.merge(child)?;
119 /// assert!(root.at("/users/1").is_ok());
120 /// # Ok(())
121 /// # }
122 /// ```
123 pub fn merge(&mut self, other: Self) -> Result<(), MergeError> {
124 let mut errors = Vec::new();
125 other.root.for_each(|path, value| {
126 if let Err(err) = self.insert(path, value) {
127 errors.push(err);
128 }
129 });
130
131 if errors.is_empty() {
132 Ok(())
133 } else {
134 Err(MergeError(errors))
135 }
136 }
137}
138
139/// A successful match consisting of the registered value
140/// and URL parameters, returned by [`Router::at`](Router::at).
141#[derive(Debug)]
142pub struct Match<V> {
143 /// The value stored under the matched node.
144 pub value: V,
145 /// The route parameters. See [parameters](crate#parameters) for more details.
146 pub params: Params,
147}