use crate::traits;
use crate::characterclasses::{PATH_UE, QUERY_UE, FRAGMENT_UE, HOST_UE};
mod resolved;
pub use resolved::RuntimeResolved;
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Discard {
All,
Some(u8),
}
pub trait CriBase {
type Scheme<'a>: traits::Scheme where Self: 'a;
type Host<'a>: traits::Host where Self: 'a;
type PathItem<'a>: traits::TextOrPet<PATH_UE> where Self: 'a;
type QueryItem<'a>: traits::TextOrPet<QUERY_UE> where Self: 'a;
type FragmentItem<'a>: traits::TextOrPet<FRAGMENT_UE> where Self: 'a;
type UserInfoItem<'a>: traits::TextOrPet<HOST_UE> where Self: 'a;
type PathIter<'a>: Iterator<Item=Self::PathItem<'a>> + ExactSizeIterator where Self: 'a;
type QueryIter<'a>: Iterator<Item=Self::QueryItem<'a>> where Self: 'a;
fn path(&self) -> Self::PathIter<'_>;
fn query(&self) -> Self::QueryIter<'_>;
fn fragment(&self) -> Option<Self::FragmentItem<'_>>;
fn userinfo(&self) -> Option<Self::UserInfoItem<'_>>;
fn host(&self) -> Self::Host<'_>;
fn port(&self) -> Option<u16>;
}
pub trait CriRef: CriBase {
fn discard(&self) -> Discard;
fn scheme(&self) -> Option<Self::Scheme<'_>>;
fn authority(&self) -> Option<traits::Authority>;
fn format_uri_ref_like(&self, w: &mut impl core::fmt::Write) -> core::fmt::Result {
use traits::*;
#[derive(PartialEq, Debug)]
enum Pending {
Not,
DiscardZero,
InsidePath,
BecauseOfAuthority,
BecauseOfScheme, BecauseOfDiscardAll, }
use Pending::*;
let mut separator_slash_pending = Not;
match self.discard() {
Discard::Some(0) => {
separator_slash_pending = DiscardZero;
}
Discard::Some(1) => {
if self.path().next().map(|p| p.contains_unescaped(':')) == Some(true) {
write!(w, "./")?;
}
}
Discard::Some(n) => {
for _ in 1..n {
write!(w, "../")?;
}
}
Discard::All => {
separator_slash_pending = BecauseOfDiscardAll;
}
}
if let Some(scheme) = self.scheme() {
write!(w, "{}:", scheme.to_text_scheme())?;
separator_slash_pending = BecauseOfScheme;
}
match self.authority() {
Some(Authority::HostPort) => {
write!(w, "//")?;
if let Some(a) = self.userinfo() {
write!(w, "{}@", a.to_uri_component())?;
}
self.host().format_uri_host(w)?;
if let Some(port) = self.port() {
write!(w, ":{}", port)?;
}
separator_slash_pending = BecauseOfAuthority;
}
Some(Authority::NoAuthoritySlashStart) => {
separator_slash_pending = BecauseOfScheme; }
Some(Authority::NoAuthoritySlashless) => {
separator_slash_pending = Not;
}
None => {
assert!(separator_slash_pending != BecauseOfScheme, "If a scheme was given, there needs to be some authority");
}
}
for p in self.path() {
match separator_slash_pending {
Not => (),
DiscardZero => write!(w, "→/")?,
BecauseOfDiscardAll | BecauseOfAuthority | BecauseOfScheme | InsidePath => write!(w, "/")?,
};
separator_slash_pending = InsidePath;
write!(w, "{}", p.to_uri_component())?;
}
let mut is_first_query = true;
for q in self.query() {
write!(w, "{}{}", if is_first_query { "?" } else { "&" }, q.to_uri_component())?;
is_first_query = false;
}
if let Some(f) = self.fragment() {
write!(w, "#{}", f.to_uri_component())?;
}
Ok(())
}
fn render_uri_ref_like(&self) -> String {
let mut s = String::new();
self.format_uri_ref_like(&mut s).expect("Strings accept all writes");
s
}
}
pub trait Cri: CriBase {
fn scheme(&self) -> Self::Scheme<'_>;
fn authority(&self) -> traits::Authority;
fn format_uri(&self, w: &mut impl core::fmt::Write) -> core::fmt::Result {
use traits::*;
write!(w, "{}:", self.scheme().to_text_scheme())?;
let mut separator_slash_pending;
match self.authority() {
Authority::HostPort => {
write!(w, "//")?;
if let Some(userinfo) = self.userinfo() {
write!(w, "{}@", userinfo.to_uri_component());
}
self.host().format_uri_host(w)?;
if let Some(port) = self.port() {
write!(w, ":{}", port)?;
}
separator_slash_pending = true;
}
Authority::NoAuthoritySlashStart => {
separator_slash_pending = true;
}
Authority::NoAuthoritySlashless => {
separator_slash_pending = false;
}
}
for p in self.path() {
if separator_slash_pending {
write!(w, "/")?;
}
separator_slash_pending = true;
write!(w, "{}", p.to_uri_component())?;
}
let mut is_first_query = true;
for q in self.query() {
write!(w, "{}{}", if is_first_query { "?" } else { "&" }, q.to_uri_component())?;
is_first_query = false;
}
if let Some(f) = self.fragment() {
write!(w, "#{}", f.to_uri_component())?;
}
Ok(())
}
fn render_uri(&self) -> String {
let mut s = String::new();
self.format_uri(&mut s).expect("Strings accept all writes");
s
}
fn resolve<R: CriRef>(&self, reference: R) -> RuntimeResolved<Self, R> {
RuntimeResolved { base: self, reference }
}
fn equals(&self, other: &impl Cri) -> bool {
use crate::traits::{TextOrPet, Scheme, Host};
fn equal_option_pet<const U: crate::characterclasses::AsciiSet>(a: Option<impl TextOrPet<U>>, b: Option<impl TextOrPet<U>>) -> bool{
match (a, b) {
(None, None) => true,
(Some(_), None) | (None, Some(_)) => false,
(Some(s), Some(o)) => s.equals(&o),
}
}
if !self.scheme().equals(other.scheme()) || self.authority() != other.authority() {
return false;
}
if self.authority() == traits::Authority::HostPort {
if !equal_option_pet(self.userinfo(), other.userinfo()) || !self.host().equals(&other.host()) || self.port() != other.port() {
return false;
}
}
if !TextOrPet::iter_equals(self.path(), other.path()) || !TextOrPet::iter_equals(self.query(), other.query()) || !equal_option_pet(self.fragment(), other.fragment()) {
return false;
}
return true;
}
}