1 use crate::tree::{denormalize_params, Node};
2 
3 use std::fmt;
4 
5 /// Represents errors that can occur when inserting a new route.
6 #[non_exhaustive]
7 #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
8 pub enum InsertError {
9     /// Attempted to insert a path that conflicts with an existing route.
10     Conflict {
11         /// The existing route that the insertion is conflicting with.
12         with: String,
13     },
14     /// Only one parameter per route segment is allowed.
15     TooManyParams,
16     /// Parameters must be registered with a name.
17     UnnamedParam,
18     /// Catch-all parameters are only allowed at the end of a path.
19     InvalidCatchAll,
20 }
21 
22 impl fmt::Display for InsertError {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result23     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24         match self {
25             Self::Conflict { with } => {
26                 write!(
27                     f,
28                     "insertion failed due to conflict with previously registered route: {}",
29                     with
30                 )
31             }
32             Self::TooManyParams => write!(f, "only one parameter is allowed per path segment"),
33             Self::UnnamedParam => write!(f, "parameters must be registered with a name"),
34             Self::InvalidCatchAll => write!(
35                 f,
36                 "catch-all parameters are only allowed at the end of a route"
37             ),
38         }
39     }
40 }
41 
42 impl std::error::Error for InsertError {}
43 
44 impl InsertError {
conflict<T>(route: &[u8], prefix: &[u8], current: &Node<T>) -> Self45     pub(crate) fn conflict<T>(route: &[u8], prefix: &[u8], current: &Node<T>) -> Self {
46         let mut route = route[..route.len() - prefix.len()].to_owned();
47 
48         if !route.ends_with(&current.prefix) {
49             route.extend_from_slice(&current.prefix);
50         }
51 
52         let mut last = current;
53         while let Some(node) = last.children.first() {
54             last = node;
55         }
56 
57         let mut current = current.children.first();
58         while let Some(node) = current {
59             route.extend_from_slice(&node.prefix);
60             current = node.children.first();
61         }
62 
63         denormalize_params(&mut route, &last.param_remapping);
64 
65         InsertError::Conflict {
66             with: String::from_utf8(route).unwrap(),
67         }
68     }
69 }
70 
71 /// A failed match attempt.
72 ///
73 /// ```
74 /// use matchit::{MatchError, Router};
75 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
76 /// let mut router = Router::new();
77 /// router.insert("/home", "Welcome!")?;
78 /// router.insert("/blog/", "Our blog.")?;
79 ///
80 /// // a route exists without the trailing slash
81 /// if let Err(err) = router.at("/home/") {
82 ///     assert_eq!(err, MatchError::ExtraTrailingSlash);
83 /// }
84 ///
85 /// // a route exists with a trailing slash
86 /// if let Err(err) = router.at("/blog") {
87 ///     assert_eq!(err, MatchError::MissingTrailingSlash);
88 /// }
89 ///
90 /// // no routes match
91 /// if let Err(err) = router.at("/foobar") {
92 ///     assert_eq!(err, MatchError::NotFound);
93 /// }
94 /// # Ok(())
95 /// # }
96 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
97 pub enum MatchError {
98     /// The path was missing a trailing slash.
99     MissingTrailingSlash,
100     /// The path had an extra trailing slash.
101     ExtraTrailingSlash,
102     /// No matching route was found.
103     NotFound,
104 }
105 
106 impl MatchError {
unsure(full_path: &[u8]) -> Self107     pub(crate) fn unsure(full_path: &[u8]) -> Self {
108         if full_path[full_path.len() - 1] == b'/' {
109             MatchError::ExtraTrailingSlash
110         } else {
111             MatchError::MissingTrailingSlash
112         }
113     }
114 }
115 
116 impl fmt::Display for MatchError {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result117     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118         let msg = match self {
119             MatchError::MissingTrailingSlash => "match error: expected trailing slash",
120             MatchError::ExtraTrailingSlash => "match error: found extra trailing slash",
121             MatchError::NotFound => "match error: route not found",
122         };
123 
124         write!(f, "{}", msg)
125     }
126 }
127 
128 impl std::error::Error for MatchError {}
129