leyline-http 0.1.5

HTTP client that impersonates Chrome, Firefox, and Safari on the wire: TLS (JA3/JA4), HTTP/2, and HTTP/3 fingerprints
Documentation
use url::Url;

use crate::core::error::{Error, Kind, Result};

mod sealed {
    pub trait Sealed {}
}

pub trait IntoUrl: sealed::Sealed {
    fn into_url(self) -> Result<Url>;

    #[doc(hidden)]
    fn into_url_with_base(self, base: Option<&Url>) -> Result<Url>
    where
        Self: Sized,
    {
        let _ = base;
        self.into_url()
    }
}

fn parse_with_base(raw: &str, base: Option<&Url>) -> Result<Url> {
    match (Url::parse(raw), base) {
        (Err(url::ParseError::RelativeUrlWithoutBase), Some(base))
            if !raw.chars().any(|c| c.is_ascii_control()) =>
        {
            base.join(raw).map_err(Error::from_url_parse)
        }
        _ => raw.into_url(),
    }
}

impl sealed::Sealed for &str {}
impl IntoUrl for &str {
    fn into_url(self) -> Result<Url> {
        if self.chars().any(|c| c.is_ascii_control()) {
            return Err(Error::new(Kind::Url).with_message("URL contains a control character"));
        }
        Url::parse(self).map_err(Error::from_url_parse)
    }

    fn into_url_with_base(self, base: Option<&Url>) -> Result<Url> {
        parse_with_base(self, base)
    }
}

impl sealed::Sealed for String {}
impl IntoUrl for String {
    fn into_url(self) -> Result<Url> {
        self.as_str().into_url()
    }

    fn into_url_with_base(self, base: Option<&Url>) -> Result<Url> {
        parse_with_base(&self, base)
    }
}

impl sealed::Sealed for &String {}
impl IntoUrl for &String {
    fn into_url(self) -> Result<Url> {
        self.as_str().into_url()
    }

    fn into_url_with_base(self, base: Option<&Url>) -> Result<Url> {
        parse_with_base(self, base)
    }
}

impl sealed::Sealed for Url {}
impl IntoUrl for Url {
    fn into_url(self) -> Result<Url> {
        Ok(self)
    }
}

impl sealed::Sealed for &Url {}
impl IntoUrl for &Url {
    fn into_url(self) -> Result<Url> {
        Ok(self.clone())
    }
}