use std::hash::{self, Hash};
use static_regular_grammar::RegularGrammar;
use crate::{
common::{parse, RiRefBufImpl, RiRefImpl},
InvalidIri, InvalidUri, Iri, IriBuf, IriRef, IriRefBuf, Uri, UriBuf,
};
use super::{bytestr_eq, Authority, AuthorityMut, Fragment, Path, PathBuf, PathMut, Query, Scheme};
#[derive(RegularGrammar)]
#[grammar(
file = "src/uri/grammar.abnf",
entry_point = "URI-reference",
name = "URI reference",
cache = "automata/uri/reference.aut.cbor",
ascii
)]
#[grammar(sized(
UriRefBuf,
derive(Debug, Display, PartialEq, Eq, PartialOrd, Ord, Hash)
))]
#[cfg_attr(feature = "serde", grammar(serde))]
#[cfg_attr(feature = "ignore-grammars", grammar(disable))]
pub struct UriRef([u8]);
impl RiRefImpl for UriRef {
type Authority = Authority;
type Path = Path;
type Query = Query;
type Fragment = Fragment;
type RiRefBuf = UriRefBuf;
fn as_bytes(&self) -> &[u8] {
&self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UriRefParts<'a> {
pub scheme: Option<&'a Scheme>,
pub authority: Option<&'a Authority>,
pub path: &'a Path,
pub query: Option<&'a Query>,
pub fragment: Option<&'a Fragment>,
}
impl UriRef {
pub fn parts(&self) -> UriRefParts {
let bytes = self.as_bytes();
let ranges = parse::reference_parts(bytes, 0);
UriRefParts {
scheme: ranges
.scheme
.map(|r| unsafe { Scheme::new_unchecked(&bytes[r]) }),
authority: ranges
.authority
.map(|r| unsafe { Authority::new_unchecked(&self.0[r]) }),
path: unsafe { Path::new_unchecked(&self.0[ranges.path]) },
query: ranges
.query
.map(|r| unsafe { Query::new_unchecked(&self.0[r]) }),
fragment: ranges
.fragment
.map(|r| unsafe { Fragment::new_unchecked(&self.0[r]) }),
}
}
#[inline]
pub fn as_uri(&self) -> Option<&Uri> {
if self.scheme().is_some() {
Some(unsafe { Uri::new_unchecked(&self.0) })
} else {
None
}
}
#[inline]
pub fn as_iri(&self) -> Option<&Iri> {
if self.scheme().is_some() {
Some(unsafe { Iri::new_unchecked(std::str::from_utf8_unchecked(&self.0)) })
} else {
None
}
}
#[inline]
pub fn as_iri_ref(&self) -> &IriRef {
unsafe { IriRef::new_unchecked(std::str::from_utf8_unchecked(&self.0)) }
}
#[inline]
pub fn scheme(&self) -> Option<&Scheme> {
RiRefImpl::scheme_opt(self)
}
pub fn authority(&self) -> Option<&Authority> {
RiRefImpl::authority(self)
}
pub fn path(&self) -> &Path {
RiRefImpl::path(self)
}
pub fn query(&self) -> Option<&Query> {
RiRefImpl::query(self)
}
pub fn fragment(&self) -> Option<&Fragment> {
RiRefImpl::fragment(self)
}
#[inline]
pub fn resolved(&self, base_iri: &(impl ?Sized + AsRef<Uri>)) -> UriBuf {
let iri_ref = self.to_owned();
iri_ref.into_resolved(base_iri)
}
pub fn relative_to(&self, other: &(impl ?Sized + AsRef<UriRef>)) -> UriRefBuf {
RiRefImpl::relative_to(self, other.as_ref())
}
#[inline]
pub fn suffix(
&self,
prefix: &(impl ?Sized + AsRef<UriRef>),
) -> Option<(PathBuf, Option<&Query>, Option<&Fragment>)> {
RiRefImpl::suffix(self, prefix.as_ref())
}
#[inline]
pub fn base(&self) -> &Self {
unsafe { Self::new_unchecked(RiRefImpl::base(self)) }
}
}
impl AsRef<IriRef> for UriRef {
fn as_ref(&self) -> &IriRef {
self.as_iri_ref()
}
}
impl<'a> From<&'a UriRef> for &'a IriRef {
fn from(value: &'a UriRef) -> Self {
value.as_iri_ref()
}
}
impl<'a> TryFrom<&'a UriRef> for &'a Uri {
type Error = InvalidUri<&'a UriRef>;
fn try_from(value: &'a UriRef) -> Result<Self, Self::Error> {
value.as_uri().ok_or(InvalidUri(value))
}
}
impl<'a> TryFrom<&'a UriRef> for &'a Iri {
type Error = InvalidIri<&'a UriRef>;
fn try_from(value: &'a UriRef) -> Result<Self, Self::Error> {
value.as_iri().ok_or(InvalidIri(value))
}
}
bytestr_eq!(UriRef);
impl PartialEq for UriRef {
fn eq(&self, other: &Self) -> bool {
self.parts() == other.parts()
}
}
impl<'a> PartialEq<&'a UriRef> for UriRef {
fn eq(&self, other: &&'a Self) -> bool {
*self == **other
}
}
impl PartialEq<UriRefBuf> for UriRef {
fn eq(&self, other: &UriRefBuf) -> bool {
*self == *other.as_uri_ref()
}
}
impl PartialEq<Uri> for UriRef {
fn eq(&self, other: &Uri) -> bool {
*self == *other.as_uri_ref()
}
}
impl<'a> PartialEq<&'a Uri> for UriRef {
fn eq(&self, other: &&'a Uri) -> bool {
*self == *other.as_uri_ref()
}
}
impl PartialEq<UriBuf> for UriRef {
fn eq(&self, other: &UriBuf) -> bool {
*self == *other.as_uri_ref()
}
}
impl Eq for UriRef {}
impl PartialOrd for UriRef {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<'a> PartialOrd<&'a UriRef> for UriRef {
fn partial_cmp(&self, other: &&'a Self) -> Option<std::cmp::Ordering> {
self.partial_cmp(*other)
}
}
impl PartialOrd<UriRefBuf> for UriRef {
fn partial_cmp(&self, other: &UriRefBuf) -> Option<std::cmp::Ordering> {
self.partial_cmp(other.as_uri_ref())
}
}
impl PartialOrd<Uri> for UriRef {
fn partial_cmp(&self, other: &Uri) -> Option<std::cmp::Ordering> {
self.partial_cmp(other.as_uri_ref())
}
}
impl<'a> PartialOrd<&'a Uri> for UriRef {
fn partial_cmp(&self, other: &&'a Uri) -> Option<std::cmp::Ordering> {
self.partial_cmp(other.as_uri_ref())
}
}
impl PartialOrd<UriBuf> for UriRef {
fn partial_cmp(&self, other: &UriBuf) -> Option<std::cmp::Ordering> {
self.partial_cmp(other.as_uri_ref())
}
}
impl Ord for UriRef {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.parts().cmp(&other.parts())
}
}
impl Hash for UriRef {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.parts().hash(state)
}
}
impl RiRefImpl for UriRefBuf {
type Authority = Authority;
type Path = Path;
type Query = Query;
type Fragment = Fragment;
type RiRefBuf = Self;
fn as_bytes(&self) -> &[u8] {
&self.0
}
}
impl RiRefBufImpl for UriRefBuf {
type Ri = Uri;
type RiBuf = UriBuf;
unsafe fn new_unchecked(bytes: Vec<u8>) -> Self {
Self::new_unchecked(bytes)
}
unsafe fn as_mut_vec(&mut self) -> &mut Vec<u8> {
&mut self.0
}
fn into_bytes(self) -> Vec<u8> {
self.0
}
}
impl UriRefBuf {
pub unsafe fn as_mut_vec(&mut self) -> &mut Vec<u8> {
&mut self.0
}
pub fn into_iri_ref(self) -> IriRefBuf {
unsafe { IriRefBuf::new_unchecked(String::from_utf8_unchecked(self.0)) }
}
pub fn try_into_uri(self) -> Result<UriBuf, InvalidUri<Self>> {
if self.scheme().is_some() {
unsafe { Ok(UriBuf::new_unchecked(self.0)) }
} else {
Err(InvalidUri(self))
}
}
pub fn try_into_iri(self) -> Result<IriBuf, InvalidIri<Self>> {
if self.scheme().is_some() {
unsafe { Ok(IriBuf::new_unchecked(String::from_utf8_unchecked(self.0))) }
} else {
Err(InvalidIri(self))
}
}
pub fn path_mut(&mut self) -> PathMut {
PathMut::from_impl(RiRefBufImpl::path_mut(self))
}
pub fn authority_mut(&mut self) -> Option<AuthorityMut> {
RiRefBufImpl::authority_mut(self).map(AuthorityMut::from_impl)
}
pub fn set_scheme(&mut self, scheme: Option<&Scheme>) {
RiRefBufImpl::set_scheme(self, scheme)
}
pub fn set_authority(&mut self, authority: Option<&Authority>) {
RiRefBufImpl::set_authority(self, authority)
}
pub fn set_path(&mut self, path: &Path) {
RiRefBufImpl::set_path(self, path)
}
pub fn set_query(&mut self, query: Option<&Query>) {
RiRefBufImpl::set_query(self, query)
}
pub fn set_fragment(&mut self, fragment: Option<&Fragment>) {
RiRefBufImpl::set_fragment(self, fragment)
}
pub fn resolve(&mut self, base_iri: &(impl ?Sized + AsRef<Uri>)) {
RiRefBufImpl::resolve(self, base_iri.as_ref())
}
pub fn into_resolved(self, base_iri: &(impl ?Sized + AsRef<Uri>)) -> UriBuf {
RiRefBufImpl::into_resolved(self, base_iri.as_ref())
}
}
impl AsRef<IriRef> for UriRefBuf {
fn as_ref(&self) -> &IriRef {
self.as_iri_ref()
}
}
impl From<UriRefBuf> for IriRefBuf {
fn from(value: UriRefBuf) -> Self {
value.into_iri_ref()
}
}
impl TryFrom<UriRefBuf> for UriBuf {
type Error = InvalidUri<UriRefBuf>;
fn try_from(value: UriRefBuf) -> Result<Self, Self::Error> {
value.try_into_uri()
}
}
impl TryFrom<UriRefBuf> for IriBuf {
type Error = InvalidIri<UriRefBuf>;
fn try_from(value: UriRefBuf) -> Result<Self, Self::Error> {
value.try_into_iri()
}
}
bytestr_eq!(UriRefBuf);
impl PartialEq<Uri> for UriRefBuf {
fn eq(&self, other: &Uri) -> bool {
*self.as_uri_ref() == *other.as_uri_ref()
}
}
impl<'a> PartialEq<&'a Uri> for UriRefBuf {
fn eq(&self, other: &&'a Uri) -> bool {
*self.as_uri_ref() == *other.as_uri_ref()
}
}
impl PartialEq<UriBuf> for UriRefBuf {
fn eq(&self, other: &UriBuf) -> bool {
*self.as_uri_ref() == *other.as_uri_ref()
}
}
impl PartialOrd<Uri> for UriRefBuf {
fn partial_cmp(&self, other: &Uri) -> Option<std::cmp::Ordering> {
self.as_uri_ref().partial_cmp(other.as_uri_ref())
}
}
impl<'a> PartialOrd<&'a Uri> for UriRefBuf {
fn partial_cmp(&self, other: &&'a Uri) -> Option<std::cmp::Ordering> {
self.as_uri_ref().partial_cmp(other.as_uri_ref())
}
}
impl PartialOrd<UriBuf> for UriRefBuf {
fn partial_cmp(&self, other: &UriBuf) -> Option<std::cmp::Ordering> {
self.as_uri_ref().partial_cmp(other.as_uri_ref())
}
}
#[cfg(test)]
mod tests {
use super::*;
const PARTS: [(
&[u8],
(
Option<&[u8]>,
Option<&[u8]>,
&[u8],
Option<&[u8]>,
Option<&[u8]>,
),
); 36] = [
(b"", (None, None, b"", None, None)),
(b"scheme:", (Some(b"scheme"), None, b"", None, None)),
(b"//authority", (None, Some(b"authority"), b"", None, None)),
(b"path", (None, None, b"path", None, None)),
(b"/path", (None, None, b"/path", None, None)),
(b"/", (None, None, b"/", None, None)),
(b"foo//bar", (None, None, b"foo//bar", None, None)),
(b"?query", (None, None, b"", Some(b"query"), None)),
(b"#fragment", (None, None, b"", None, Some(b"fragment"))),
(
b"scheme:?query",
(Some(b"scheme"), None, b"", Some(b"query"), None),
),
(
b"scheme://authority",
(Some(b"scheme"), Some(b"authority"), b"", None, None),
),
(b"scheme:path", (Some(b"scheme"), None, b"path", None, None)),
(
b"scheme:/path",
(Some(b"scheme"), None, b"/path", None, None),
),
(
b"scheme:?query",
(Some(b"scheme"), None, b"", Some(b"query"), None),
),
(
b"scheme:#fragment",
(Some(b"scheme"), None, b"", None, Some(b"fragment")),
),
(
b"//authority/path",
(None, Some(b"authority"), b"/path", None, None),
),
(
b"//authority?query",
(None, Some(b"authority"), b"", Some(b"query"), None),
),
(
b"//authority#fragment",
(None, Some(b"authority"), b"", None, Some(b"fragment")),
),
(b"path?query", (None, None, b"path", Some(b"query"), None)),
(b"/path?query", (None, None, b"/path", Some(b"query"), None)),
(
b"path#fragment",
(None, None, b"path", None, Some(b"fragment")),
),
(
b"?query#fragment",
(None, None, b"", Some(b"query"), Some(b"fragment")),
),
(
b"scheme://authority/path",
(Some(b"scheme"), Some(b"authority"), b"/path", None, None),
),
(
b"scheme://authority?query",
(
Some(b"scheme"),
Some(b"authority"),
b"",
Some(b"query"),
None,
),
),
(
b"scheme://authority#fragment",
(
Some(b"scheme"),
Some(b"authority"),
b"",
None,
Some(b"fragment"),
),
),
(
b"scheme:path?query",
(Some(b"scheme"), None, b"path", Some(b"query"), None),
),
(
b"scheme:path#fragment",
(Some(b"scheme"), None, b"path", None, Some(b"fragment")),
),
(
b"//authority/path?query",
(None, Some(b"authority"), b"/path", Some(b"query"), None),
),
(
b"//authority/path#fragment",
(None, Some(b"authority"), b"/path", None, Some(b"fragment")),
),
(
b"//authority?query#fragment",
(
None,
Some(b"authority"),
b"",
Some(b"query"),
Some(b"fragment"),
),
),
(
b"path?query#fragment",
(None, None, b"path", Some(b"query"), Some(b"fragment")),
),
(
b"scheme://authority/path?query",
(
Some(b"scheme"),
Some(b"authority"),
b"/path",
Some(b"query"),
None,
),
),
(
b"scheme://authority/path#fragment",
(
Some(b"scheme"),
Some(b"authority"),
b"/path",
None,
Some(b"fragment"),
),
),
(
b"scheme://authority?query#fragment",
(
Some(b"scheme"),
Some(b"authority"),
b"",
Some(b"query"),
Some(b"fragment"),
),
),
(
b"scheme:path?query#fragment",
(
Some(b"scheme"),
None,
b"path",
Some(b"query"),
Some(b"fragment"),
),
),
(
b"scheme://authority/path?query#fragment",
(
Some(b"scheme"),
Some(b"authority"),
b"/path",
Some(b"query"),
Some(b"fragment"),
),
),
];
#[test]
fn parts() {
for (input, expected) in PARTS {
let input = UriRef::new(input).unwrap();
let parts = input.parts();
assert_eq!(parts.scheme.map(Scheme::as_bytes), expected.0);
assert_eq!(parts.authority.map(Authority::as_bytes), expected.1);
assert_eq!(parts.path.as_bytes(), expected.2);
assert_eq!(parts.query.map(Query::as_bytes), expected.3);
assert_eq!(parts.fragment.map(Fragment::as_bytes), expected.4)
}
}
#[test]
fn scheme() {
for (input, expected) in PARTS {
let input = UriRef::new(input).unwrap();
assert_eq!(input.scheme().map(Scheme::as_bytes), expected.0)
}
}
#[test]
fn authority() {
for (input, expected) in PARTS {
let input = UriRef::new(input).unwrap();
assert_eq!(input.authority().map(Authority::as_bytes), expected.1)
}
}
#[test]
fn set_authority() {
let vectors: [(&[u8], Option<&[u8]>, &[u8]); 3] = [
(
b"scheme:/path",
Some(b"authority"),
b"scheme://authority/path",
),
(
b"scheme:path",
Some(b"authority"),
b"scheme://authority/path",
),
(b"scheme://authority//path", None, b"scheme:/.//path"),
];
for (input, authority, expected) in vectors {
let mut buffer = UriRefBuf::new(input.to_vec()).unwrap();
let authority = authority.map(Authority::new).transpose().unwrap();
buffer.set_authority(authority);
assert_eq!(buffer.as_bytes(), expected)
}
}
#[test]
fn path() {
for (input, expected) in PARTS {
let input = UriRef::new(input).unwrap();
assert_eq!(input.path().as_bytes(), expected.2)
}
}
#[test]
fn query() {
for (input, expected) in PARTS {
let input = UriRef::new(input).unwrap();
assert_eq!(input.query().map(Query::as_bytes), expected.3)
}
}
#[test]
fn fragment() {
for (input, expected) in PARTS {
let input = UriRef::new(input).unwrap();
assert_eq!(input.fragment().map(Fragment::as_bytes), expected.4)
}
}
}