use core::marker::PhantomData;
#[cfg(test)]
use core::num::ParseIntError;
use core::{hash, iter};
use ref_cast::{RefCastCustom, ref_cast_custom};
use crate::imp::{AuthMeta, HostMeta};
use crate::pct_enc::encoder::{IRegName, IUserinfo, Port};
#[cfg(test)]
use crate::pct_enc::table;
use crate::pct_enc::{EStr, Encoder};
pub(crate) type IAuthority<'a> = Authority<'a, IUserinfo, IRegName>;
#[derive(RefCastCustom)]
#[repr(transparent)]
pub(crate) struct Scheme {
inner: str,
}
const ASCII_CASE_MASK: u8 = 0b0010_0000;
impl Scheme {
#[ref_cast_custom]
#[inline]
pub(crate) const fn new_validated(scheme: &str) -> &Self;
#[inline]
#[must_use]
#[cfg(test)]
pub(crate) const fn new_or_panic(s: &str) -> &Self {
match Self::new(s) {
Some(scheme) => scheme,
None => panic!("invalid scheme"),
}
}
#[inline]
#[must_use]
#[cfg(test)]
pub(crate) const fn new(s: &str) -> Option<&Self> {
if matches!(s.as_bytes(), [first, rem @ ..]
if first.is_ascii_alphabetic() && table::SCHEME.validate(rem))
{
Some(Self::new_validated(s))
} else {
None
}
}
#[inline]
#[must_use]
pub(crate) fn as_str(&self) -> &str {
&self.inner
}
}
impl PartialEq for Scheme {
#[inline]
fn eq(&self, other: &Self) -> bool {
let (a, b) = (self.inner.as_bytes(), other.inner.as_bytes());
a.len() == b.len()
&& iter::zip(a, b).all(|(x, y)| x | ASCII_CASE_MASK == y | ASCII_CASE_MASK)
}
}
impl Eq for Scheme {}
impl hash::Hash for Scheme {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
let mut buf = [0; 8];
for chunk in self.inner.as_bytes().chunks(8) {
let len = chunk.len();
for i in 0..len {
buf[i] = chunk[i] | ASCII_CASE_MASK;
}
state.write(&buf[..len]);
}
}
}
impl core::fmt::Debug for Scheme {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::Debug::fmt(self.as_str(), f)
}
}
impl core::fmt::Display for Scheme {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::Display::fmt(self.as_str(), f)
}
}
#[derive(Clone, Copy)]
struct AuthorityInner<'a> {
val: &'a str,
meta: AuthMeta,
}
impl<'a> AuthorityInner<'a> {
fn userinfo(&self) -> Option<&'a EStr<IUserinfo>> {
let host_start = self.meta.host_bounds.0;
(host_start != 0).then(|| EStr::new_validated(&self.val[..host_start - 1]))
}
fn host(&self) -> &'a str {
let (start, end) = self.meta.host_bounds;
&self.val[start..end]
}
fn port(&self) -> Option<&'a EStr<Port>> {
let host_end = self.meta.host_bounds.1;
(host_end != self.val.len()).then(|| EStr::new_validated(&self.val[host_end + 1..]))
}
#[cfg(test)]
fn port_to_u16(&self) -> Result<Option<u16>, ParseIntError> {
self.port()
.filter(|s| !s.is_empty())
.map(|s| s.as_str().parse())
.transpose()
}
}
#[derive(Clone, Copy)]
pub(crate) struct Authority<'a, UserinfoE = IUserinfo, RegNameE = IRegName> {
inner: AuthorityInner<'a>,
_marker: PhantomData<(UserinfoE, RegNameE)>,
}
impl<'a, UserinfoE: Encoder, RegNameE: Encoder> Authority<'a, UserinfoE, RegNameE> {
pub(crate) const fn new(val: &'a str, meta: AuthMeta) -> Self {
Self {
inner: AuthorityInner { val, meta },
_marker: PhantomData,
}
}
pub(crate) fn meta(&self) -> AuthMeta {
self.inner.meta
}
#[inline]
#[must_use]
pub(crate) fn as_str(&self) -> &'a str {
self.inner.val
}
#[must_use]
pub(crate) fn userinfo(&self) -> Option<&'a EStr<UserinfoE>> {
self.inner.userinfo().map(EStr::cast)
}
#[must_use]
pub(crate) fn host(&self) -> &'a str {
self.inner.host()
}
#[must_use]
pub(crate) fn host_parsed(&self) -> Host<'a, RegNameE> {
match self.inner.meta.host_meta {
HostMeta::Ipv4 => Host::Ipv4,
HostMeta::Ipv6 => Host::Ipv6,
HostMeta::IpvFuture => Host::IpvFuture,
HostMeta::RegName => Host::RegName(EStr::new_validated(self.host())),
}
}
#[must_use]
pub(crate) fn port(&self) -> Option<&'a EStr<Port>> {
self.inner.port()
}
#[cfg(test)]
pub(crate) fn port_to_u16(&self) -> Result<Option<u16>, ParseIntError> {
self.inner.port_to_u16()
}
}
impl<UserinfoE: Encoder, RegNameE: Encoder> core::fmt::Debug
for Authority<'_, UserinfoE, RegNameE>
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Authority")
.field("userinfo", &self.userinfo())
.field("host", &self.host())
.field("host_parsed", &self.host_parsed())
.field("port", &self.port())
.finish()
}
}
impl core::fmt::Display for Authority<'_> {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::Display::fmt(self.as_str(), f)
}
}
#[derive(Clone, Copy)]
pub(crate) enum Host<'a, RegNameE: Encoder = IRegName> {
#[non_exhaustive]
Ipv4,
#[non_exhaustive]
Ipv6,
#[non_exhaustive]
IpvFuture,
RegName(&'a EStr<RegNameE>),
}
impl<RegNameE: Encoder> core::fmt::Debug for Host<'_, RegNameE> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Host::Ipv4 => f.debug_struct("Ipv4").finish_non_exhaustive(),
Host::Ipv6 => f.debug_struct("Ipv6").finish_non_exhaustive(),
Host::IpvFuture => f.debug_struct("IpvFuture").finish_non_exhaustive(),
Host::RegName(name) => f.debug_tuple("RegName").field(name).finish(),
}
}
}