use std::borrow::Cow;
use crate::RawStr;
use crate::ext::IntoOwned;
use crate::uri::{Authority, Data, Origin, Absolute, Asterisk};
use crate::uri::{Path, Query, Error, as_utf8_unchecked, fmt};
use crate::parse::{Extent, IndexedStr};
#[derive(Debug, Clone)]
pub struct Reference<'a> {
source: Option<Cow<'a, str>>,
scheme: Option<IndexedStr<'a>>,
authority: Option<Authority<'a>>,
path: Data<'a, fmt::Path>,
query: Option<Data<'a, fmt::Query>>,
fragment: Option<IndexedStr<'a>>,
}
impl<'a> Reference<'a> {
#[inline]
pub(crate) unsafe fn raw(
source: Cow<'a, [u8]>,
scheme: Option<Extent<&'a [u8]>>,
authority: Option<Authority<'a>>,
path: Extent<&'a [u8]>,
query: Option<Extent<&'a [u8]>>,
fragment: Option<Extent<&'a [u8]>>,
) -> Reference<'a> {
Reference {
source: Some(as_utf8_unchecked(source)),
scheme: scheme.map(|s| s.into()),
authority,
path: Data::raw(path),
query: query.map(Data::raw),
fragment: fragment.map(|f| f.into()),
}
}
#[cfg(test)]
pub fn new(
scheme: impl Into<Option<&'a str>>,
auth: impl Into<Option<Authority<'a>>>,
path: &'a str,
query: impl Into<Option<&'a str>>,
frag: impl Into<Option<&'a str>>,
) -> Reference<'a> {
Reference::const_new(scheme.into(), auth.into(), path, query.into(), frag.into())
}
#[doc(hidden)]
pub const fn const_new(
scheme: Option<&'a str>,
authority: Option<Authority<'a>>,
path: &'a str,
query: Option<&'a str>,
fragment: Option<&'a str>,
) -> Reference<'a> {
Reference {
source: None,
scheme: match scheme {
Some(scheme) => Some(IndexedStr::Concrete(Cow::Borrowed(scheme))),
None => None
},
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,
},
fragment: match fragment {
Some(frag) => Some(IndexedStr::Concrete(Cow::Borrowed(frag))),
None => None,
},
}
}
pub fn parse(string: &'a str) -> Result<Reference<'a>, Error<'a>> {
crate::parse::uri::reference_from_str(string)
}
pub fn parse_owned(string: String) -> Result<Reference<'static>, Error<'static>> {
let uri_ref = Reference::parse(&string).map_err(|e| e.into_owned())?;
debug_assert!(uri_ref.source.is_some(), "Reference parsed w/o source");
Ok(Reference {
scheme: uri_ref.scheme.into_owned(),
authority: uri_ref.authority.into_owned(),
path: uri_ref.path.into_owned(),
query: uri_ref.query.into_owned(),
fragment: uri_ref.fragment.into_owned(),
source: Some(Cow::Owned(string)),
})
}
#[inline]
pub fn scheme(&self) -> Option<&str> {
self.scheme.as_ref().map(|s| s.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 fragment(&self) -> Option<&RawStr> {
self.fragment.as_ref()
.map(|frag| frag.from_cow_source(&self.source).into())
}
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
}
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 with_query_fragment_of(mut self, other: Reference<'a>) -> Self {
if let Some(query) = other.query {
if self.query().is_none() {
self.query = Some(Data::new(query.value.into_concrete(&self.source)));
}
}
if let Some(frag) = other.fragment {
if self.fragment().is_none() {
self.fragment = Some(IndexedStr::from(frag.into_concrete(&self.source)));
}
}
self
}
}
impl_traits!(Reference, authority, scheme, path, query, fragment);
impl_serde!(Reference<'a>, "a URI-reference");
impl<'a> From<Absolute<'a>> for Reference<'a> {
fn from(absolute: Absolute<'a>) -> Self {
Reference {
source: absolute.source,
scheme: Some(absolute.scheme),
authority: absolute.authority,
path: absolute.path,
query: absolute.query,
fragment: None,
}
}
}
impl<'a> From<Origin<'a>> for Reference<'a> {
fn from(origin: Origin<'a>) -> Self {
Reference {
source: origin.source,
scheme: None,
authority: None,
path: origin.path,
query: origin.query,
fragment: None,
}
}
}
impl<'a> From<Authority<'a>> for Reference<'a> {
fn from(authority: Authority<'a>) -> Self {
Reference {
source: match authority.source {
Some(Cow::Borrowed(b)) => Some(Cow::Borrowed(b)),
_ => None
},
authority: Some(authority),
scheme: None,
path: Data::new(""),
query: None,
fragment: None,
}
}
}
impl From<Asterisk> for Reference<'_> {
fn from(_: Asterisk) -> Self {
Reference {
source: None,
authority: None,
scheme: None,
path: Data::new("*"),
query: None,
fragment: None,
}
}
}
impl std::fmt::Display for Reference<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(scheme) = self.scheme() {
write!(f, "{}:", scheme)?;
}
if let Some(authority) = self.authority() {
write!(f, "//{}", authority)?;
}
write!(f, "{}", self.path())?;
if let Some(query) = self.query() {
write!(f, "?{}", query)?;
}
if let Some(frag) = self.fragment() {
write!(f, "#{}", frag)?;
}
Ok(())
}
}