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(¤t.prefix) { 49 route.extend_from_slice(¤t.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