use std::borrow::Cow;
use crate::ext::IntoOwned;
use crate::parse::{Extent, IndexedStr};
use crate::uri::{Authority, Path, Query, Data, Error, as_utf8_unchecked, fmt};
#[derive(Debug, Clone)]
pub struct Absolute<'a> {
pub(crate) source: Option<Cow<'a, str>>,
pub(crate) scheme: IndexedStr<'a>,
pub(crate) authority: Option<Authority<'a>>,
pub(crate) path: Data<'a, fmt::Path>,
pub(crate) query: Option<Data<'a, fmt::Query>>,
}
impl<'a> Absolute<'a> {
pub fn parse(string: &'a str) -> Result<Absolute<'a>, Error<'a>> {
crate::parse::uri::absolute_from_str(string)
}
pub fn parse_owned(string: String) -> Result<Absolute<'static>, Error<'static>> {
let absolute = Absolute::parse(&string).map_err(|e| e.into_owned())?;
debug_assert!(absolute.source.is_some(), "Absolute parsed w/o source");
let absolute = Absolute {
scheme: absolute.scheme.into_owned(),
authority: absolute.authority.into_owned(),
query: absolute.query.into_owned(),
path: absolute.path.into_owned(),
source: Some(Cow::Owned(string)),
};
Ok(absolute)
}
#[inline(always)]
pub fn scheme(&self) -> &str {
self.scheme.from_cow_source(&self.source)
}
#[inline(always)]
pub fn authority(&self) -> Option<&Authority<'a>> {
self.authority.as_ref()
}
#[inline(always)]
pub fn path(&self) -> Path<'_> {
Path { source: &self.source, data: &self.path }
}
#[inline(always)]
pub fn query(&self) -> Option<Query<'_>> {
self.query.as_ref().map(|data| Query { source: &self.source, data })
}
#[inline(always)]
pub fn clear_query(&mut self) {
self.set_query(None);
}
pub fn is_normalized(&self) -> bool {
let normalized_query = self.query().map_or(true, |q| q.is_normalized());
if self.authority().is_some() && !self.path().is_empty() {
self.path().is_normalized(true)
&& self.path() != "/"
&& normalized_query
} else {
self.path().is_normalized(false) && normalized_query
}
}
pub fn normalize(&mut self) {
if self.authority().is_some() && !self.path().is_empty() {
if self.path() == "/" {
self.set_path("");
} else if !self.path().is_normalized(true) {
self.path = self.path().to_normalized(true);
}
} else {
self.path = self.path().to_normalized(false);
}
if let Some(query) = self.query() {
if !query.is_normalized() {
self.query = query.to_normalized();
}
}
}
pub fn into_normalized(mut self) -> Self {
self.normalize();
self
}
#[inline(always)]
pub fn set_authority(&mut self, authority: Authority<'a>) {
self.authority = Some(authority);
}
#[inline(always)]
pub fn with_authority(mut self, authority: Authority<'a>) -> Self {
self.set_authority(authority);
self
}
}
#[doc(hidden)]
impl<'a> Absolute<'a> {
#[inline]
pub(crate) unsafe fn raw(
source: Cow<'a, [u8]>,
scheme: Extent<&'a [u8]>,
authority: Option<Authority<'a>>,
path: Extent<&'a [u8]>,
query: Option<Extent<&'a [u8]>>,
) -> Absolute<'a> {
Absolute {
source: Some(as_utf8_unchecked(source)),
scheme: scheme.into(),
authority,
path: Data::raw(path),
query: query.map(Data::raw)
}
}
#[cfg(test)]
pub fn new(
scheme: &'a str,
authority: impl Into<Option<Authority<'a>>>,
path: &'a str,
query: impl Into<Option<&'a str>>,
) -> Absolute<'a> {
assert!(!scheme.is_empty());
Absolute::const_new(scheme, authority.into(), path, query.into())
}
#[doc(hidden)]
pub const fn const_new(
scheme: &'a str,
authority: Option<Authority<'a>>,
path: &'a str,
query: Option<&'a str>,
) -> Absolute<'a> {
Absolute {
source: None,
scheme: IndexedStr::Concrete(Cow::Borrowed(scheme)),
authority,
path: Data {
value: IndexedStr::Concrete(Cow::Borrowed(path)),
decoded_segments: state::InitCell::new(),
},
query: match query {
Some(query) => Some(Data {
value: IndexedStr::Concrete(Cow::Borrowed(query)),
decoded_segments: state::InitCell::new(),
}),
None => None,
},
}
}
pub(crate) fn set_path<P>(&mut self, path: P)
where P: Into<Cow<'a, str>>
{
self.path = Data::new(path.into());
}
pub(crate) fn set_query<Q: Into<Option<Cow<'a, str>>>>(&mut self, query: Q) {
self.query = query.into().map(Data::new);
}
}
impl_serde!(Absolute<'a>, "an absolute-form URI");
impl_traits!(Absolute, scheme, authority, path, query);
impl std::fmt::Display for Absolute<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}:", self.scheme())?;
if let Some(authority) = self.authority() {
write!(f, "//{}", authority)?;
}
write!(f, "{}", self.path())?;
if let Some(query) = self.query() {
write!(f, "?{}", query)?;
}
Ok(())
}
}