rama_net/http/uri/match_replace/
mod.rs1use std::{borrow::Cow, sync::Arc};
4
5use crate::uri::Uri;
6
7use rama_core::error::BoxError;
8use rama_utils::collections::smallvec::SmallVec;
9use rama_utils::thirdparty::wildcard::Wildcard;
10
11mod fmt;
12
13mod rule;
14pub use rule::UriMatchReplaceRule;
15
16mod rule_set;
17pub use rule_set::UriMatchReplaceRuleset;
18
19mod scheme;
20pub use scheme::UriMatchReplaceScheme;
21
22mod domain;
23pub use domain::UriMatchReplaceDomain;
24
25mod slice;
26mod tuple;
27
28mod fallthrough;
29pub use fallthrough::UriMatchReplaceFallthrough;
30
31pub trait UriMatchReplace {
108 fn match_replace_uri<'a>(&self, uri: Cow<'a, Uri>) -> Result<Cow<'a, Uri>, UriMatchError<'a>>;
114}
115
116#[derive(Debug)]
117pub enum UriMatchError<'a> {
121 NoMatch(Cow<'a, Uri>),
127 Unexpected(BoxError),
130}
131
132impl core::fmt::Display for UriMatchError<'_> {
133 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
134 match self {
135 UriMatchError::NoMatch(cow) => write!(f, "uri match error: no match: uri = {cow}"),
136 UriMatchError::Unexpected(err) => write!(f, "uri match error: unexpected: {err}"),
137 }
138 }
139}
140
141impl core::error::Error for UriMatchError<'_> {
142 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
143 match self {
144 UriMatchError::NoMatch(_) => None,
145 UriMatchError::Unexpected(err) => err.source(),
146 }
147 }
148}
149
150#[derive(Debug, Clone, Copy, Default)]
151#[non_exhaustive]
152pub struct UriMatchReplaceNever;
154
155impl UriMatchReplaceNever {
156 #[must_use]
157 pub fn new() -> Self {
158 Self
159 }
160}
161
162impl UriMatchReplace for UriMatchReplaceNever {
164 #[inline]
165 fn match_replace_uri<'a>(&self, uri: Cow<'a, Uri>) -> Result<Cow<'a, Uri>, UriMatchError<'a>> {
166 Err(UriMatchError::NoMatch(uri))
167 }
168}
169
170impl<R: UriMatchReplace> UriMatchReplace for &R {
171 #[inline(always)]
172 fn match_replace_uri<'a>(&self, uri: Cow<'a, Uri>) -> Result<Cow<'a, Uri>, UriMatchError<'a>> {
173 (*self).match_replace_uri(uri)
174 }
175}
176
177impl<R: UriMatchReplace> UriMatchReplace for Arc<R> {
178 #[inline(always)]
179 fn match_replace_uri<'a>(&self, uri: Cow<'a, Uri>) -> Result<Cow<'a, Uri>, UriMatchError<'a>> {
180 (**self).match_replace_uri(uri)
181 }
182}
183
184impl UriMatchReplace for Arc<dyn UriMatchReplace> {
185 #[inline(always)]
186 fn match_replace_uri<'a>(&self, uri: Cow<'a, Uri>) -> Result<Cow<'a, Uri>, UriMatchError<'a>> {
187 (**self).match_replace_uri(uri)
188 }
189}
190
191impl UriMatchReplace for Box<dyn UriMatchReplace> {
192 #[inline(always)]
193 fn match_replace_uri<'a>(&self, uri: Cow<'a, Uri>) -> Result<Cow<'a, Uri>, UriMatchError<'a>> {
194 (**self).match_replace_uri(uri)
195 }
196}
197
198impl<R: UriMatchReplace> UriMatchReplace for Option<R> {
199 #[inline]
200 fn match_replace_uri<'a>(&self, uri: Cow<'a, Uri>) -> Result<Cow<'a, Uri>, UriMatchError<'a>> {
201 match self {
202 Some(r) => r.match_replace_uri(uri),
203 None => Err(UriMatchError::NoMatch(uri)),
204 }
205 }
206}
207
208macro_rules! impl_uri_match_replace_either {
209 ($id:ident, $($param:ident),+ $(,)?) => {
210 impl<$($param),+> UriMatchReplace for rama_core::combinators::$id<$($param),+>
211 where
212 $($param: UriMatchReplace),+,
213 {
214 fn match_replace_uri<'a>(&self, uri: Cow<'a, Uri>,
215 ) -> Result<Cow<'a, Uri>, UriMatchError<'a>> {
216 match self {
217 $(
218 rama_core::combinators::$id::$param(r) => r.match_replace_uri(uri),
219 )+
220 }
221 }
222 }
223 };
224}
225
226rama_core::combinators::impl_either!(impl_uri_match_replace_either);
227
228#[expect(private_bounds)]
231pub trait TryIntoPattern: private_ptn::TryIntoPatternPriv {}
232
233impl TryIntoPattern for &'static str {}
234impl TryIntoPattern for String {}
235impl TryIntoPattern for &'static [u8] {}
236impl TryIntoPattern for Vec<u8> {}
237
238#[derive(Debug)]
239struct Pattern {
241 wildcard: Wildcard<'static>,
242 include_query: bool,
243}
244
245#[expect(private_bounds)]
248pub trait TryIntoUriFmt: private_fmt::TryIntoUriFmtPriv {}
249
250impl TryIntoUriFmt for &'static str {}
251impl TryIntoUriFmt for String {}
252impl TryIntoUriFmt for &'static [u8] {}
253impl TryIntoUriFmt for Vec<u8> {}
254
255mod private_ptn {
256 use super::*;
257
258 use rama_core::error::{BoxError, ErrorContext as _};
259 use rama_utils::{str::submatch_ignore_ascii_case, thirdparty::wildcard::WildcardBuilder};
260
261 pub(super) trait TryIntoPatternPriv {
262 fn try_into_wildcard(self) -> Result<Pattern, BoxError>;
263 }
264
265 impl TryIntoPatternPriv for &'static str {
266 #[inline(always)]
267 fn try_into_wildcard(self) -> Result<Pattern, BoxError> {
268 self.as_bytes().try_into_wildcard()
269 }
270 }
271
272 impl TryIntoPatternPriv for &'static [u8] {
273 fn try_into_wildcard(self) -> Result<Pattern, BoxError> {
274 let wildcard = WildcardBuilder::new(self)
275 .case_insensitive(true)
276 .build()
277 .context("build pattern from static slice")?;
278 let include_query = submatch_ignore_ascii_case(self, b"\\?");
279 Ok(Pattern {
280 wildcard,
281 include_query,
282 })
283 }
284 }
285
286 impl TryIntoPatternPriv for String {
287 #[inline]
288 fn try_into_wildcard(self) -> Result<Pattern, BoxError> {
289 self.into_bytes().try_into_wildcard()
290 }
291 }
292
293 impl TryIntoPatternPriv for Vec<u8> {
294 #[inline]
295 fn try_into_wildcard(self) -> Result<Pattern, BoxError> {
296 let wildcard = WildcardBuilder::from_owned(self)
297 .case_insensitive(true)
298 .build()
299 .context("build pattern from heap slice")?;
300 let include_query = submatch_ignore_ascii_case(wildcard.pattern(), b"\\?");
301 Ok(Pattern {
302 wildcard,
303 include_query,
304 })
305 }
306 }
307}
308
309mod private_fmt {
310 use super::*;
311
312 use rama_core::error::BoxError;
313
314 pub(super) trait TryIntoUriFmtPriv {
315 fn try_into_fmt(self) -> Result<fmt::UriFormatter, BoxError>;
316 }
317
318 impl TryIntoUriFmtPriv for &'static str {
319 #[inline(always)]
320 fn try_into_fmt(self) -> Result<fmt::UriFormatter, BoxError> {
321 self.as_bytes().try_into_fmt()
322 }
323 }
324
325 impl TryIntoUriFmtPriv for &'static [u8] {
326 #[inline(always)]
327 fn try_into_fmt(self) -> Result<fmt::UriFormatter, BoxError> {
328 fmt::UriFormatter::try_new(self.into())
329 }
330 }
331
332 impl TryIntoUriFmtPriv for String {
333 #[inline(always)]
334 fn try_into_fmt(self) -> Result<fmt::UriFormatter, BoxError> {
335 self.into_bytes().try_into_fmt()
336 }
337 }
338
339 impl TryIntoUriFmtPriv for Vec<u8> {
340 #[inline(always)]
341 fn try_into_fmt(self) -> Result<fmt::UriFormatter, BoxError> {
342 fmt::UriFormatter::try_new(self.into())
343 }
344 }
345}
346
347type SmallUriStr = SmallVec<[u8; 128]>;