use std::{borrow::Cow, sync::Arc};
use crate::uri::Uri;
use rama_core::error::BoxError;
use rama_utils::collections::smallvec::SmallVec;
use rama_utils::thirdparty::wildcard::Wildcard;
mod fmt;
mod rule;
pub use rule::UriMatchReplaceRule;
mod rule_set;
pub use rule_set::UriMatchReplaceRuleset;
mod scheme;
pub use scheme::UriMatchReplaceScheme;
mod domain;
pub use domain::UriMatchReplaceDomain;
mod slice;
mod tuple;
mod fallthrough;
pub use fallthrough::UriMatchReplaceFallthrough;
pub trait UriMatchReplace {
fn match_replace_uri<'a>(&self, uri: Cow<'a, Uri>) -> Result<Cow<'a, Uri>, UriMatchError<'a>>;
}
#[derive(Debug)]
pub enum UriMatchError<'a> {
NoMatch(Cow<'a, Uri>),
Unexpected(BoxError),
}
impl core::fmt::Display for UriMatchError<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
UriMatchError::NoMatch(cow) => write!(f, "uri match error: no match: uri = {cow}"),
UriMatchError::Unexpected(err) => write!(f, "uri match error: unexpected: {err}"),
}
}
}
impl core::error::Error for UriMatchError<'_> {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
match self {
UriMatchError::NoMatch(_) => None,
UriMatchError::Unexpected(err) => err.source(),
}
}
}
#[derive(Debug, Clone, Copy, Default)]
#[non_exhaustive]
pub struct UriMatchReplaceNever;
impl UriMatchReplaceNever {
#[must_use]
pub fn new() -> Self {
Self
}
}
impl UriMatchReplace for UriMatchReplaceNever {
#[inline]
fn match_replace_uri<'a>(&self, uri: Cow<'a, Uri>) -> Result<Cow<'a, Uri>, UriMatchError<'a>> {
Err(UriMatchError::NoMatch(uri))
}
}
impl<R: UriMatchReplace> UriMatchReplace for &R {
#[inline(always)]
fn match_replace_uri<'a>(&self, uri: Cow<'a, Uri>) -> Result<Cow<'a, Uri>, UriMatchError<'a>> {
(*self).match_replace_uri(uri)
}
}
impl<R: UriMatchReplace> UriMatchReplace for Arc<R> {
#[inline(always)]
fn match_replace_uri<'a>(&self, uri: Cow<'a, Uri>) -> Result<Cow<'a, Uri>, UriMatchError<'a>> {
(**self).match_replace_uri(uri)
}
}
impl UriMatchReplace for Arc<dyn UriMatchReplace> {
#[inline(always)]
fn match_replace_uri<'a>(&self, uri: Cow<'a, Uri>) -> Result<Cow<'a, Uri>, UriMatchError<'a>> {
(**self).match_replace_uri(uri)
}
}
impl UriMatchReplace for Box<dyn UriMatchReplace> {
#[inline(always)]
fn match_replace_uri<'a>(&self, uri: Cow<'a, Uri>) -> Result<Cow<'a, Uri>, UriMatchError<'a>> {
(**self).match_replace_uri(uri)
}
}
impl<R: UriMatchReplace> UriMatchReplace for Option<R> {
#[inline]
fn match_replace_uri<'a>(&self, uri: Cow<'a, Uri>) -> Result<Cow<'a, Uri>, UriMatchError<'a>> {
match self {
Some(r) => r.match_replace_uri(uri),
None => Err(UriMatchError::NoMatch(uri)),
}
}
}
macro_rules! impl_uri_match_replace_either {
($id:ident, $($param:ident),+ $(,)?) => {
impl<$($param),+> UriMatchReplace for rama_core::combinators::$id<$($param),+>
where
$($param: UriMatchReplace),+,
{
fn match_replace_uri<'a>(&self, uri: Cow<'a, Uri>,
) -> Result<Cow<'a, Uri>, UriMatchError<'a>> {
match self {
$(
rama_core::combinators::$id::$param(r) => r.match_replace_uri(uri),
)+
}
}
}
};
}
rama_core::combinators::impl_either!(impl_uri_match_replace_either);
#[expect(private_bounds)]
pub trait TryIntoPattern: private_ptn::TryIntoPatternPriv {}
impl TryIntoPattern for &'static str {}
impl TryIntoPattern for String {}
impl TryIntoPattern for &'static [u8] {}
impl TryIntoPattern for Vec<u8> {}
#[derive(Debug)]
struct Pattern {
wildcard: Wildcard<'static>,
include_query: bool,
}
#[expect(private_bounds)]
pub trait TryIntoUriFmt: private_fmt::TryIntoUriFmtPriv {}
impl TryIntoUriFmt for &'static str {}
impl TryIntoUriFmt for String {}
impl TryIntoUriFmt for &'static [u8] {}
impl TryIntoUriFmt for Vec<u8> {}
mod private_ptn {
use super::*;
use rama_core::error::{BoxError, ErrorContext as _};
use rama_utils::{str::submatch_ignore_ascii_case, thirdparty::wildcard::WildcardBuilder};
pub(super) trait TryIntoPatternPriv {
fn try_into_wildcard(self) -> Result<Pattern, BoxError>;
}
impl TryIntoPatternPriv for &'static str {
#[inline(always)]
fn try_into_wildcard(self) -> Result<Pattern, BoxError> {
self.as_bytes().try_into_wildcard()
}
}
impl TryIntoPatternPriv for &'static [u8] {
fn try_into_wildcard(self) -> Result<Pattern, BoxError> {
let wildcard = WildcardBuilder::new(self)
.case_insensitive(true)
.build()
.context("build pattern from static slice")?;
let include_query = submatch_ignore_ascii_case(self, b"\\?");
Ok(Pattern {
wildcard,
include_query,
})
}
}
impl TryIntoPatternPriv for String {
#[inline]
fn try_into_wildcard(self) -> Result<Pattern, BoxError> {
self.into_bytes().try_into_wildcard()
}
}
impl TryIntoPatternPriv for Vec<u8> {
#[inline]
fn try_into_wildcard(self) -> Result<Pattern, BoxError> {
let wildcard = WildcardBuilder::from_owned(self)
.case_insensitive(true)
.build()
.context("build pattern from heap slice")?;
let include_query = submatch_ignore_ascii_case(wildcard.pattern(), b"\\?");
Ok(Pattern {
wildcard,
include_query,
})
}
}
}
mod private_fmt {
use super::*;
use rama_core::error::BoxError;
pub(super) trait TryIntoUriFmtPriv {
fn try_into_fmt(self) -> Result<fmt::UriFormatter, BoxError>;
}
impl TryIntoUriFmtPriv for &'static str {
#[inline(always)]
fn try_into_fmt(self) -> Result<fmt::UriFormatter, BoxError> {
self.as_bytes().try_into_fmt()
}
}
impl TryIntoUriFmtPriv for &'static [u8] {
#[inline(always)]
fn try_into_fmt(self) -> Result<fmt::UriFormatter, BoxError> {
fmt::UriFormatter::try_new(self.into())
}
}
impl TryIntoUriFmtPriv for String {
#[inline(always)]
fn try_into_fmt(self) -> Result<fmt::UriFormatter, BoxError> {
self.into_bytes().try_into_fmt()
}
}
impl TryIntoUriFmtPriv for Vec<u8> {
#[inline(always)]
fn try_into_fmt(self) -> Result<fmt::UriFormatter, BoxError> {
fmt::UriFormatter::try_new(self.into())
}
}
}
type SmallUriStr = SmallVec<[u8; 128]>;