diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index c3cea5c8f..cddc79b5f 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -157,7 +157,7 @@ where } } -impl<'a, T> IntoVecString for &'a Vec +impl IntoVecString for &Vec where T: Into + Clone, { diff --git a/crates/core/src/routing/filters/path.rs b/crates/core/src/routing/filters/path.rs index 93339cfc1..1ba7d6da9 100644 --- a/crates/core/src/routing/filters/path.rs +++ b/crates/core/src/routing/filters/path.rs @@ -251,7 +251,7 @@ impl Debug for CharsWisp { } } impl PathWisp for CharsWisp { - fn detect<'a>(&self, state: &mut PathState) -> bool { + fn detect(&self, state: &mut PathState) -> bool { let Some(picked) = state.pick() else { return false; }; @@ -300,11 +300,13 @@ pub struct CombWisp { wild_regex: Option, wild_start: Option, } -impl TryFrom> for CombWisp { - type Error = String; - #[inline] - fn try_from(wisps: Vec) -> Result { - let mut comb_regex = "".to_owned(); +impl CombWisp { + /// Create new `CombWisp`. + /// + /// # Panics + /// If contains unsupported `WispKind``. + pub fn new(wisps: Vec) -> Result { + let mut comb_regex = "^".to_owned(); let mut names = Vec::with_capacity(wisps.len()); let mut is_prev_named = false; let mut is_greedy = false; @@ -339,10 +341,7 @@ impl TryFrom> for CombWisp { if wisp.0.starts_with('*') { is_greedy = true; let (star_mark, name) = crate::routing::split_wild_name(&wisp.0); - wild_regex = Some( - Regex::new(&format!("(?<{}>.*)", ®ex::escape(name))) - .expect("regex should worked"), - ); + wild_regex = Some(Regex::new(".*").expect("regex should worked")); wild_start = Some(star_mark.to_owned()); names.push(name.to_owned()); } else { @@ -361,15 +360,18 @@ impl TryFrom> for CombWisp { if wisp.name.starts_with('*') { is_greedy = true; let (star_mark, name) = crate::routing::split_wild_name(&wisp.name); - wild_regex = Some( - Regex::new(&format!("(?<{}>.*)", ®ex::escape(name))) - .expect("regex should work"), - ); + wild_regex = Some(wisp.regex); wild_start = Some(star_mark.to_owned()); names.push(name.to_owned()); + } else { + let regex = wisp + .regex + .as_str() + .trim_start_matches('^') + .trim_end_matches('$'); + comb_regex.push_str(&format!("(?<{}>{})", wisp.name, regex)); + names.push(wisp.name); } - comb_regex.push_str(&format!("(?<{}>{})", wisp.name, wisp.regex.as_str())); - names.push(wisp.name); } WispKind::Chars(wisp) => { return Err(format!( @@ -382,6 +384,9 @@ impl TryFrom> for CombWisp { } } } + if wild_regex.is_none() { + comb_regex.push('$'); + } Regex::new(&comb_regex) .map(|comb_regex| Self { names, @@ -392,23 +397,9 @@ impl TryFrom> for CombWisp { .map_err(|e| format!("Regex error: {}", e)) } } -impl CombWisp { - /// Create new `CombWisp`. - /// - /// # Panics - /// If contains unsupported `WispKind``. - pub fn new(wisps: Vec) -> Self { - match Self::try_from(wisps) { - Ok(comb) => comb, - Err(e) => { - panic!("failed to build CombWisp: {}", e); - } - } - } -} impl PathWisp for CombWisp { #[inline] - fn detect<'a>(&self, state: &mut PathState) -> bool { + fn detect(&self, state: &mut PathState) -> bool { let Some(picked) = state.pick().map(|s| s.to_owned()) else { return false; }; @@ -435,9 +426,6 @@ impl PathWisp for CombWisp { } } let len = if let Some(cap) = caps.get(0) { - if cap.start() != 0 { - return false; - } cap.as_str().len() } else { return false; @@ -462,14 +450,10 @@ impl PathWisp for CombWisp { if !wild_path.is_empty() || !wild_start.starts_with("*+") { let cap = wild_regex.captures(&wild_path).and_then(|caps| caps.get(0)); if let Some(cap) = cap { - if cap.start() != 0 { - false - } else { - let cap = cap.as_str().to_owned(); - state.forward(cap.len()); - state.params.insert(wild_name, cap); - true - } + let cap = cap.as_str().to_owned(); + state.forward(cap.len()); + state.params.insert(wild_name, cap); + true } else { false } @@ -487,7 +471,7 @@ impl PathWisp for CombWisp { pub struct NamedWisp(pub String); impl PathWisp for NamedWisp { #[inline] - fn detect<'a>(&self, state: &mut PathState) -> bool { + fn detect(&self, state: &mut PathState) -> bool { if self.0.starts_with('*') { let rest = state.all_rest().unwrap_or_default(); if self.0.starts_with("*?") @@ -530,8 +514,21 @@ pub struct RegexWisp { } impl RegexWisp { #[inline] - fn new(name: String, regex: Regex) -> RegexWisp { - RegexWisp { name, regex } + fn new(name: String, regex: &str) -> Result { + let regex = if !regex.starts_with('^') { + &*format!("^{}", regex) + } else { + regex + }; + let regex = if !regex.ends_with('$') { + &*format!("{}$", regex) + } else { + regex + }; + Ok(Self { + name, + regex: Regex::new(regex).map_err(|e| format!("invalid regex: `{}`, {}", regex, e))?, + }) } } impl PartialEq for RegexWisp { @@ -542,7 +539,7 @@ impl PartialEq for RegexWisp { } impl PathWisp for RegexWisp { #[inline] - fn detect<'a>(&self, state: &mut PathState) -> bool { + fn detect(&self, state: &mut PathState) -> bool { if self.name.starts_with('*') { let rest = state.all_rest().unwrap_or_default(); if self.name.starts_with("*?") @@ -557,9 +554,6 @@ impl PathWisp for RegexWisp { let cap = self.regex.captures(&rest).and_then(|caps| caps.get(0)); if let Some(cap) = cap { - if cap.start() != 0 { - return false; - } let cap = cap.as_str().to_owned(); state.forward(cap.len()); state.params.insert(&self.name, cap); @@ -592,7 +586,7 @@ impl PathWisp for RegexWisp { pub struct ConstWisp(pub String); impl PathWisp for ConstWisp { #[inline] - fn detect<'a>(&self, state: &mut PathState) -> bool { + fn detect(&self, state: &mut PathState) -> bool { let Some(picked) = state.pick() else { return false; }; @@ -820,8 +814,8 @@ impl PathParser { wisps.push(builder.build(name, sign, args)?); } else { self.next(false); - let regex = Regex::new(&self.scan_regex()?).map_err(|e| e.to_string())?; - wisps.push(RegexWisp::new(name, regex).into()); + let regex = &self.scan_regex()?; + wisps.push(RegexWisp::new(name, regex)?.into()); } } else if ch == '>' { wisps.push(NamedWisp(name).into()); @@ -872,7 +866,7 @@ impl PathParser { } let mut scaned = self.scan_wisps()?; if scaned.len() > 1 { - wisps.push(CombWisp::try_from(scaned)?.into()); + wisps.push(CombWisp::new(scaned)?.into()); } else if let Some(wisp) = scaned.pop() { wisps.push(wisp); } else { @@ -1064,7 +1058,7 @@ mod tests { let segments = PathParser::new(r"/").parse().unwrap(); assert_eq!( format!("{:?}", segments), - r#"[RegexWisp { name: "abc", regex: Regex("\\d+") }]"# + r#"[RegexWisp { name: "abc", regex: Regex("^\\d+$") }]"# ); } #[test] @@ -1072,7 +1066,7 @@ mod tests { let segments = PathParser::new(r"/").parse().unwrap(); assert_eq!( format!("{:?}", segments), - r#"[RegexWisp { name: "abc", regex: Regex("\\d+/.+") }]"# + r#"[RegexWisp { name: "abc", regex: Regex("^\\d+/.+$") }]"# ); } #[test] @@ -1080,7 +1074,7 @@ mod tests { let segments = PathParser::new(r"/prefix_").parse().unwrap(); assert_eq!( format!("{:?}", segments), - r#"[CombWisp { names: ["abc"], comb_regex: Regex("prefix_(?\\d+)"), wild_regex: None, wild_start: None }]"# + r#"[CombWisp { names: ["abc"], comb_regex: Regex("^prefix_(?\\d+)$"), wild_regex: None, wild_start: None }]"# ); } #[test] @@ -1088,7 +1082,7 @@ mod tests { let segments = PathParser::new(r"/_suffix.png").parse().unwrap(); assert_eq!( format!("{:?}", segments), - r#"[CombWisp { names: ["abc"], comb_regex: Regex("(?\\d+)_suffix\\.png"), wild_regex: None, wild_start: None }]"# + r#"[CombWisp { names: ["abc"], comb_regex: Regex("^(?\\d+)_suffix\\.png$"), wild_regex: None, wild_start: None }]"# ); } #[test] @@ -1098,7 +1092,7 @@ mod tests { .unwrap(); assert_eq!( format!("{:?}", segments), - r#"[CombWisp { names: ["abc"], comb_regex: Regex("prefix(?\\d+)suffix\\.png"), wild_regex: None, wild_start: None }]"# + r#"[CombWisp { names: ["abc"], comb_regex: Regex("^prefix(?\\d+)suffix\\.png$"), wild_regex: None, wild_start: None }]"# ); } #[test] @@ -1108,7 +1102,7 @@ mod tests { .unwrap(); assert_eq!( format!("{:?}", segments), - r#"[NamedWisp("pid"), ConstWisp("show"), CombWisp { names: ["table_name"], comb_regex: Regex("(?.*)\\.bu"), wild_regex: None, wild_start: None }]"# + r#"[NamedWisp("pid"), ConstWisp("show"), CombWisp { names: ["table_name"], comb_regex: Regex("^(?.*)\\.bu$"), wild_regex: None, wild_start: None }]"# ); } #[test] @@ -1118,7 +1112,7 @@ mod tests { .unwrap(); assert_eq!( format!("{:?}", segments), - r#"[CombWisp { names: ["id"], comb_regex: Regex("first(?.*)"), wild_regex: None, wild_start: None }, CombWisp { names: ["abc"], comb_regex: Regex("prefix(?\\d+)"), wild_regex: None, wild_start: None }]"# + r#"[CombWisp { names: ["id"], comb_regex: Regex("^first(?.*)$"), wild_regex: None, wild_start: None }, CombWisp { names: ["abc"], comb_regex: Regex("^prefix(?\\d+)$"), wild_regex: None, wild_start: None }]"# ); } #[test] @@ -1128,7 +1122,7 @@ mod tests { .unwrap(); assert_eq!( format!("{:?}", segments), - r#"[CombWisp { names: ["id"], comb_regex: Regex("first(?.*)"), wild_regex: None, wild_start: None }, CombWisp { names: ["abc"], comb_regex: Regex("prefix(?\\d+)"), wild_regex: None, wild_start: None }]"# + r#"[CombWisp { names: ["id"], comb_regex: Regex("^first(?.*)$"), wild_regex: None, wild_start: None }, CombWisp { names: ["abc"], comb_regex: Regex("^prefix(?\\d+)$"), wild_regex: None, wild_start: None }]"# ); } #[test] @@ -1138,7 +1132,7 @@ mod tests { .unwrap(); assert_eq!( format!("{:?}", segments), - r#"[CombWisp { names: ["id"], comb_regex: Regex("first(?\\d+)"), wild_regex: None, wild_start: None }, CombWisp { names: ["abc"], comb_regex: Regex("prefix(?\\d+)"), wild_regex: None, wild_start: None }]"# + r#"[CombWisp { names: ["id"], comb_regex: Regex("^first(?\\d+)$"), wild_regex: None, wild_start: None }, CombWisp { names: ["abc"], comb_regex: Regex("^prefix(?\\d+)$"), wild_regex: None, wild_start: None }]"# ); } #[test] @@ -1148,7 +1142,7 @@ mod tests { .unwrap(); assert_eq!( format!("{:?}", segments), - r#"[CombWisp { names: ["id"], comb_regex: Regex("first(?.*)"), wild_regex: None, wild_start: None }, CombWisp { names: ["abc"], comb_regex: Regex("prefix(?\\d+)ext"), wild_regex: None, wild_start: None }]"# + r#"[CombWisp { names: ["id"], comb_regex: Regex("^first(?.*)$"), wild_regex: None, wild_start: None }, CombWisp { names: ["abc"], comb_regex: Regex("^prefix(?\\d+)ext$"), wild_regex: None, wild_start: None }]"# ); } #[test] @@ -1156,19 +1150,19 @@ mod tests { let segments = PathParser::new(r"/first/<**rest>").parse().unwrap(); assert_eq!( format!("{:?}", segments), - r#"[CombWisp { names: ["id"], comb_regex: Regex("first(?.*)"), wild_regex: None, wild_start: None }, NamedWisp("**rest")]"# + r#"[CombWisp { names: ["id"], comb_regex: Regex("^first(?.*)$"), wild_regex: None, wild_start: None }, NamedWisp("**rest")]"# ); let segments = PathParser::new(r"/first/<*+rest>").parse().unwrap(); assert_eq!( format!("{:?}", segments), - r#"[CombWisp { names: ["id"], comb_regex: Regex("first(?.*)"), wild_regex: None, wild_start: None }, NamedWisp("*+rest")]"# + r#"[CombWisp { names: ["id"], comb_regex: Regex("^first(?.*)$"), wild_regex: None, wild_start: None }, NamedWisp("*+rest")]"# ); let segments = PathParser::new(r"/first/<*?rest>").parse().unwrap(); assert_eq!( format!("{:?}", segments), - r#"[CombWisp { names: ["id"], comb_regex: Regex("first(?.*)"), wild_regex: None, wild_start: None }, NamedWisp("*?rest")]"# + r#"[CombWisp { names: ["id"], comb_regex: Regex("^first(?.*)$"), wild_regex: None, wild_start: None }, NamedWisp("*?rest")]"# ); } #[test] @@ -1182,6 +1176,12 @@ mod tests { #[test] fn test_parse_comb_1() { + let segments = PathParser::new(r"/firstworld<**rest>").parse().unwrap(); + assert_eq!( + format!("{:?}", segments), + r#"[CombWisp { names: ["id", "rest"], comb_regex: Regex("^first(?.*)world"), wild_regex: Some(Regex(".*")), wild_start: Some("**") }]"# + ); + let filter = PathFilter::new("/firstworld<**rest>"); let mut state = PathState::new("first123world.ext"); assert!(filter.detect(&mut state)); @@ -1209,6 +1209,17 @@ mod tests { let mut state = PathState::new("abc/hello1"); assert!(!filter.detect(&mut state)); } + #[test] + fn test_parse_comb_5() { + let filter = PathFilter::new("/abc/t<**rest:/\\d+/>"); + let mut state = PathState::new("abc/t11"); + assert!(!filter.detect(&mut state)); + + let mut state = PathState::new("abc/tlo1"); + assert!(!filter.detect(&mut state)); + let mut state = PathState::new("abc/t11a"); + assert!(!filter.detect(&mut state)); + } #[test] fn test_parse_rest2_failed() { diff --git a/crates/core/src/serde/request.rs b/crates/core/src/serde/request.rs index feb4e2764..fe2c84798 100644 --- a/crates/core/src/serde/request.rs +++ b/crates/core/src/serde/request.rs @@ -49,7 +49,7 @@ pub(crate) enum Payload<'a> { JsonStr(&'a str), JsonMap(HashMap<&'a str, &'a RawValue>), } -impl<'a> Payload<'a> { +impl Payload<'_> { #[allow(dead_code)] pub(crate) fn is_form_data(&self) -> bool { matches!(*self, Self::FormData(_)) diff --git a/crates/core/src/writing/mod.rs b/crates/core/src/writing/mod.rs index 86956fb4a..0b5233380 100644 --- a/crates/core/src/writing/mod.rs +++ b/crates/core/src/writing/mod.rs @@ -104,7 +104,7 @@ impl Scribe for &'static str { res.write_body(self).ok(); } } -impl<'a> Scribe for &'a String { +impl Scribe for &String { #[inline] fn render(self, res: &mut Response) { res.headers_mut().insert( diff --git a/crates/core/src/writing/text.rs b/crates/core/src/writing/text.rs index 7812e558a..c9470c798 100644 --- a/crates/core/src/writing/text.rs +++ b/crates/core/src/writing/text.rs @@ -99,7 +99,7 @@ impl Scribe for Text { res.write_body(content).ok(); } } -impl<'a> Scribe for Text<&'a String> { +impl Scribe for Text<&String> { #[inline] fn render(self, res: &mut Response) { let content = self.set_header(res); diff --git a/crates/cors/src/allow_headers.rs b/crates/cors/src/allow_headers.rs index a0530de14..ecfb379b7 100644 --- a/crates/cors/src/allow_headers.rs +++ b/crates/cors/src/allow_headers.rs @@ -122,20 +122,20 @@ impl From> for AllowHeaders { } } -impl<'a> From<&'a str> for AllowHeaders { - fn from(val: &'a str) -> Self { +impl From<&str> for AllowHeaders { + fn from(val: &str) -> Self { Self::list([HeaderName::from_str(val).expect("Invalid header name.")]) } } -impl<'a> From<&'a String> for AllowHeaders { - fn from(val: &'a String) -> Self { +impl From<&String> for AllowHeaders { + fn from(val: &String) -> Self { Self::list([HeaderName::from_str(val).expect("Invalid header name.")]) } } -impl<'a> From> for AllowHeaders { - fn from(vals: Vec<&'a str>) -> Self { +impl From> for AllowHeaders { + fn from(vals: Vec<&str>) -> Self { Self::list( vals.into_iter() .map(|v| HeaderName::from_str(v).expect("Invalid header name.")) @@ -143,8 +143,8 @@ impl<'a> From> for AllowHeaders { ) } } -impl<'a> From<&'a Vec> for AllowHeaders { - fn from(vals: &'a Vec) -> Self { +impl From<&Vec> for AllowHeaders { + fn from(vals: &Vec) -> Self { Self::list( vals.iter() .map(|v| HeaderName::from_str(v).expect("Invalid header name.")) diff --git a/crates/cors/src/allow_origin.rs b/crates/cors/src/allow_origin.rs index 557318bbc..1e7634057 100644 --- a/crates/cors/src/allow_origin.rs +++ b/crates/cors/src/allow_origin.rs @@ -133,20 +133,20 @@ impl From> for AllowOrigin { } } -impl<'a> From<&'a str> for AllowOrigin { - fn from(val: &'a str) -> Self { +impl From<&str> for AllowOrigin { + fn from(val: &str) -> Self { Self::exact(HeaderValue::from_str(val).expect("invalid `HeaderValue`")) } } -impl<'a> From<&'a String> for AllowOrigin { - fn from(val: &'a String) -> Self { +impl From<&String> for AllowOrigin { + fn from(val: &String) -> Self { Self::exact(HeaderValue::from_str(val).expect("invalid `HeaderValue`")) } } -impl<'a> From> for AllowOrigin { - fn from(vals: Vec<&'a str>) -> Self { +impl From> for AllowOrigin { + fn from(vals: Vec<&str>) -> Self { Self::list( vals.iter() .map(|v| HeaderValue::from_str(v).expect("invalid `HeaderValue`")) @@ -154,8 +154,8 @@ impl<'a> From> for AllowOrigin { ) } } -impl<'a, const N: usize> From<[&'a str; N]> for AllowOrigin { - fn from(vals: [&'a str; N]) -> Self { +impl From<[&str; N]> for AllowOrigin { + fn from(vals: [&str; N]) -> Self { Self::list( vals.iter() .map(|v| HeaderValue::from_str(v).expect("invalid `HeaderValue`")) @@ -163,8 +163,8 @@ impl<'a, const N: usize> From<[&'a str; N]> for AllowOrigin { ) } } -impl<'a> From<&'a Vec> for AllowOrigin { - fn from(vals: &'a Vec) -> Self { +impl From<&Vec> for AllowOrigin { + fn from(vals: &Vec) -> Self { Self::list( vals.iter() .map(|v| HeaderValue::from_str(v).expect("invalid `HeaderValue`")) diff --git a/crates/cors/src/expose_headers.rs b/crates/cors/src/expose_headers.rs index e9a3a32fb..4fa3f9b79 100644 --- a/crates/cors/src/expose_headers.rs +++ b/crates/cors/src/expose_headers.rs @@ -105,20 +105,20 @@ impl From> for ExposeHeaders { } } -impl<'a> From<&'a str> for ExposeHeaders { - fn from(val: &'a str) -> Self { +impl From<&str> for ExposeHeaders { + fn from(val: &str) -> Self { Self::list([HeaderName::from_str(val).expect("Invalid header name.")]) } } -impl<'a> From<&'a String> for ExposeHeaders { - fn from(val: &'a String) -> Self { +impl From<&String> for ExposeHeaders { + fn from(val: &String) -> Self { Self::list([HeaderName::from_str(val).expect("Invalid header name.")]) } } -impl<'a> From> for ExposeHeaders { - fn from(vals: Vec<&'a str>) -> Self { +impl From> for ExposeHeaders { + fn from(vals: Vec<&str>) -> Self { Self::list( vals.into_iter() .map(|v| HeaderName::from_str(v).expect("Invalid header name.")) @@ -126,8 +126,8 @@ impl<'a> From> for ExposeHeaders { ) } } -impl<'a> From<&'a Vec> for ExposeHeaders { - fn from(vals: &'a Vec) -> Self { +impl From<&Vec> for ExposeHeaders { + fn from(vals: &Vec) -> Self { Self::list( vals.iter() .map(|v| HeaderName::from_str(v).expect("Invalid header name.")) diff --git a/crates/oapi/src/endpoint.rs b/crates/oapi/src/endpoint.rs index 3531b68d9..275ed88c8 100644 --- a/crates/oapi/src/endpoint.rs +++ b/crates/oapi/src/endpoint.rs @@ -124,7 +124,7 @@ impl EndpointOutRegister for salvo_core::Error { } } -impl EndpointOutRegister for &'static str { +impl EndpointOutRegister for &str { #[inline] fn register(components: &mut Components, operation: &mut Operation) { operation.responses.insert( @@ -142,7 +142,7 @@ impl EndpointOutRegister for String { ); } } -impl<'a> EndpointOutRegister for &'a String { +impl EndpointOutRegister for &String { #[inline] fn register(components: &mut Components, operation: &mut Operation) { operation.responses.insert( diff --git a/crates/serve-static/src/dir.rs b/crates/serve-static/src/dir.rs index e2795fa07..d86e85ab7 100644 --- a/crates/serve-static/src/dir.rs +++ b/crates/serve-static/src/dir.rs @@ -79,13 +79,13 @@ pub trait StaticRoots { fn collect(self) -> Vec; } -impl<'a> StaticRoots for &'a str { +impl StaticRoots for &str { #[inline] fn collect(self) -> Vec { vec![PathBuf::from(self)] } } -impl<'a> StaticRoots for &'a String { +impl StaticRoots for &String { #[inline] fn collect(self) -> Vec { vec![PathBuf::from(self)]