use std::borrow::Cow;
use std::fmt;
use std::str::FromStr;
mod error;
pub mod format;
pub mod matcher;
pub mod path;
pub use error::{Error, Result};
use format::encoding::encode;
use format::Format;
use path::validate;
pub trait ToId {
#[allow(clippy::missing_errors_doc)]
fn to_id(&self) -> Result<Cow<Id>>;
}
#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Id {
format: Format<6>,
}
impl Id {
pub fn new<S, C, P>(scheme: S, context: C, path: P) -> Result<Self>
where
S: AsRef<[u8]>,
C: AsRef<[u8]>,
P: AsRef<[u8]>,
{
let scheme = encode(validate(scheme.as_ref())?);
let context = encode(validate(context.as_ref())?);
let path = encode(validate(path.as_ref())?);
let capacity = 8 + scheme.len() + context.len() + path.len();
let mut buffer = String::with_capacity(capacity);
buffer.push_str("zri:");
buffer.push_str(scheme.as_ref());
buffer.push_str("::");
buffer.push_str(context.as_ref());
buffer.push(':');
buffer.push_str(path.as_ref());
buffer.push(':');
buffer
.parse()
.map_err(Into::into)
.map(|format| Self { format })
}
#[inline]
pub fn set_scheme<S>(&mut self, scheme: S) -> Result<&mut Self>
where
S: AsRef<[u8]>,
{
self.format
.set(1, validate(scheme)?)
.map_err(Into::into)
.map(|()| self)
}
#[inline]
pub fn set_binding<S>(&mut self, binding: S) -> Result<&mut Self>
where
S: AsRef<[u8]>,
{
self.format
.set(2, validate(binding)?)
.map_err(Into::into)
.map(|()| self)
}
#[inline]
pub fn set_context<S>(&mut self, context: S) -> Result<&mut Self>
where
S: AsRef<[u8]>,
{
self.format
.set(3, validate(context)?)
.map_err(Into::into)
.map(|()| self)
}
#[inline]
pub fn set_path<S>(&mut self, path: S) -> Result<&mut Self>
where
S: AsRef<[u8]>,
{
self.format
.set(4, validate(path)?)
.map_err(Into::into)
.map(|()| self)
}
#[inline]
pub fn set_fragment<S>(&mut self, fragment: S) -> Result<&mut Self>
where
S: AsRef<[u8]>,
{
self.format
.set(5, validate(fragment)?)
.map_err(Into::into)
.map(|()| self)
}
}
#[allow(clippy::must_use_candidate)]
impl Id {
#[inline]
pub fn scheme(&self) -> Cow<str> {
self.format.get(1)
}
#[inline]
pub fn binding(&self) -> Option<Cow<str>> {
Some(self.format.get(2)).filter(|value| !value.is_empty())
}
#[inline]
pub fn context(&self) -> Cow<str> {
self.format.get(3)
}
#[inline]
pub fn path(&self) -> Cow<str> {
self.format.get(4)
}
#[inline]
pub fn fragment(&self) -> Option<Cow<str>> {
Some(self.format.get(5)).filter(|value| !value.is_empty())
}
}
impl ToId for &Id {
#[inline]
fn to_id(&self) -> Result<Cow<Id>> {
Ok(Cow::Borrowed(self))
}
}
impl ToId for &str {
#[inline]
fn to_id(&self) -> Result<Cow<Id>> {
self.parse().map(Cow::Owned)
}
}
impl FromStr for Id {
type Err = Error;
fn from_str(value: &str) -> Result<Self> {
let format = Format::from_str(validate(value)?)?;
if format.get(0) != "zri" {
Err(Error::Prefix)?;
}
if format.get(1).is_empty() {
Err(Error::Component("scheme"))?;
}
if format.get(3).is_empty() {
Err(Error::Component("context"))?;
}
if format.get(4).is_empty() {
Err(Error::Component("path"))?;
}
Ok(Self { format })
}
}
impl fmt::Display for Id {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.format.fmt(f)
}
}
impl fmt::Debug for Id {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Id")
.field("scheme", &self.scheme())
.field("binding", &self.binding())
.field("context", &self.context())
.field("path", &self.path())
.field("fragment", &self.fragment())
.finish()
}
}