use crate::characterclasses::{AsciiSet, HOST_UE};
pub trait Scheme {
fn to_cri_id(&self) -> Option<i16>;
fn to_text_scheme(&self) -> &str;
fn equals(&self, other: impl Scheme) -> bool {
match (self.to_cri_id(), other.to_cri_id()) {
(Some(s), Some(o)) => s == o,
_ => self.to_text_scheme() == other.to_text_scheme(),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Authority {
HostPort,
NoAuthoritySlashless,
NoAuthoritySlashStart,
}
#[derive(Debug)]
pub enum HostRef<'a, HostItem: TextOrPet<HOST_UE>, HostIter: Clone + IntoIterator<Item=HostItem>> {
IPv4(&'a no_std_net::Ipv4Addr),
IPv6 { address: &'a no_std_net::Ipv6Addr, zone: Option<&'a str> },
Hostname(HostIter),
}
impl<'a, HostItem: TextOrPet<HOST_UE>, HostIter: Clone + IntoIterator<Item=HostItem>> HostRef<'a, HostItem, HostIter> {
pub fn format_uri_host(&self, w: &mut impl core::fmt::Write) -> core::fmt::Result {
match self {
HostRef::IPv4(a) => write!(w, "{}", a)?,
HostRef::IPv6 { address, zone } => {
write!(w, "[{address}")?;
if let Some(zone) = zone {
write!(w, "%25{zone}")?;
}
write!(w, "]")?;
}
HostRef::Hostname(s) => {
let mut first = true;
for component in s.clone().into_iter() {
if first {
first = false;
} else {
write!(w, ".")?;
}
write!(w, "{}", component.to_uri_component())?;
}
}
}
Ok(())
}
}
pub trait Host {
type HostItem<'a>: TextOrPet<HOST_UE> where Self: 'a;
type HostIter<'a>: Clone + IntoIterator<Item=Self::HostItem<'a>> where Self: 'a;
fn as_ref(&self) -> HostRef<'_, Self::HostItem<'_>, Self::HostIter<'_>>;
fn format_uri_host(&self, w: &mut impl core::fmt::Write) -> core::fmt::Result {
self.as_ref().format_uri_host(w)
}
fn equals(&self, other: &impl Host) -> bool {
match (self.as_ref(), other.as_ref()) {
(HostRef::IPv4(s), HostRef::IPv4(o)) => s == o,
(HostRef::IPv6 { address: sa, zone: sz }, HostRef::IPv6 { address: oa, zone: oz }) => sa == oa && sz == oz,
(HostRef::Hostname(s), HostRef::Hostname(o)) => TextOrPet::iter_equals(s.into_iter(), o.into_iter()),
_ => false,
}
}
}
impl Scheme for ! {
fn to_cri_id(&self) -> Option<i16> {
*self
}
fn to_text_scheme(&self) -> &str {
*self
}
}
pub trait TextOrPet<const UNESCAPED: AsciiSet> {
type UriEncoded<'a>: core::fmt::Display where Self: 'a;
fn to_uri_component(&self) -> Self::UriEncoded<'_>;
fn contains_unescaped(&self, needle: char) -> bool {
use core::fmt::Write;
struct SeenCharacter {
seen: bool,
needle: char,
}
impl core::fmt::Write for SeenCharacter {
fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> {
self.seen |= s.contains(self.needle);
Ok(())
}
}
let mut result = SeenCharacter { seen: false, needle };
write!(result, "{}", self.to_uri_component())
.expect("SeenCharacter never fails writes");
result.seen
}
fn equals(&self, other: &impl TextOrPet<UNESCAPED>) -> bool {
let s = format!("{}", self.to_uri_component());
let o = format!("{}", other.to_uri_component());
s == o
}
fn iter_equals<O: TextOrPet<UNESCAPED>>(mut s: impl Iterator<Item=Self>, mut o: impl Iterator<Item=O>) -> bool where Self: Sized {
loop {
match (s.next().as_ref(), o.next().as_ref()) {
(None, None) => { return true; }
(Some(si), Some(oi)) => if !si.equals(oi) { return false; }
_ => { return false; }
}
}
}
}