#[cfg(not(feature = "std"))]
use alloc::format;
use crate::{Name, pname::PrefixedName};
use core::{
fmt::{Display, Formatter, Result as FmtResult},
str::FromStr,
};
use strum::{EnumIs, EnumTryAs};
#[cfg(feature = "genid")]
use crate::error::Error;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct Iri(url::Url);
#[derive(Clone, Debug, PartialEq, Eq, EnumIs, EnumTryAs)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[allow(missing_docs)] pub enum IriRef {
Iri(Iri),
PrefixedName(PrefixedName),
}
impl Display for Iri {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
if f.alternate() {
write!(f, "{}", self.0)
} else {
write!(f, "<{}>", self.0)
}
}
}
impl FromStr for Iri {
type Err = url::ParseError;
#[inline(always)]
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = if s.starts_with('<') && s.ends_with('>') {
s.strip_prefix('<').unwrap().strip_suffix('>').unwrap()
} else {
s
};
Ok(Self(url::Url::from_str(s)?))
}
}
impl TryFrom<&str> for Iri {
type Error = url::ParseError;
#[inline(always)]
fn try_from(s: &str) -> Result<Self, Self::Error> {
Ok(Self::from_str(s)?)
}
}
impl From<url::Url> for Iri {
fn from(value: url::Url) -> Self {
Self(value)
}
}
impl From<&url::Url> for Iri {
fn from(value: &url::Url) -> Self {
Self(value.clone())
}
}
impl From<Iri> for url::Url {
fn from(value: Iri) -> Self {
value.0
}
}
impl From<&Iri> for url::Url {
fn from(value: &Iri) -> Self {
value.0.clone()
}
}
impl AsRef<url::Url> for Iri {
fn as_ref(&self) -> &url::Url {
&self.0
}
}
impl AsMut<url::Url> for Iri {
fn as_mut(&mut self) -> &mut url::Url {
&mut self.0
}
}
impl AsRef<str> for Iri {
fn as_ref(&self) -> &str {
self.0.as_ref()
}
}
impl Iri {
pub fn with_new_path<S>(&self, path: S) -> Self
where
S: AsRef<str>,
{
let mut new_self = self.clone();
new_self.0.set_path(path.as_ref());
new_self
}
pub fn with_new_fragment<S>(&self, fragment: S) -> Self
where
S: AsRef<str>,
{
let mut new_self = self.clone();
new_self.0.set_fragment(Some(fragment.as_ref()));
new_self
}
pub fn with_empty_fragment(&self) -> Self {
self.with_new_fragment("")
}
pub fn with_no_fragment(&self) -> Self {
let mut new_self = self.clone();
new_self.0.set_fragment(None);
new_self
}
pub fn looks_like_namespace(&self) -> bool {
self.0.fragment() == Some("") || (self.0.path().ends_with("/") && self.0.query().is_none())
}
pub fn split(&self) -> Option<(Self, Name)>
where
Self: Sized,
{
if self.0.fragment().map(|s| !s.is_empty()).unwrap_or_default() {
if let Ok(name) = Name::from_str(self.0.fragment().unwrap()) {
Some((self.with_empty_fragment(), name))
} else {
None
}
} else if !self.0.path().is_empty()
&& !self.0.path().ends_with("/")
&& self.0.query().is_none()
{
if let Ok(name) = Name::from_str(self.0.path_segments().unwrap().last().unwrap()) {
let path = self.0.path();
let path = &path[0..path.len() - name.as_ref().len()];
Some((self.with_new_path(path), name))
} else {
None
}
} else {
None
}
}
pub fn namespace(&self) -> Option<Self>
where
Self: Sized,
{
self.split().map(|(u, _)| u)
}
pub fn name(&self) -> Option<Name>
where
Self: Sized,
{
self.split().map(|(_, n)| n)
}
pub fn make_name(&self, name: Name) -> Option<Self>
where
Self: Sized,
{
if self.0.fragment().is_some() {
Some(self.with_new_fragment(name.as_ref()))
} else if self.0.path().ends_with("/") && self.0.query().is_none() {
Some(self.with_new_path(format!("{}{}", self.0.path(), name.as_ref())))
} else {
None
}
}
#[cfg(feature = "genid")]
pub fn genid(&self) -> Result<Self, Error>
where
Self: Sized,
{
let new_uuid = uuid::Uuid::new_v4();
let new_uuid = new_uuid
.simple()
.encode_lower(&mut uuid::Uuid::encode_buffer())
.to_string();
let path = format!("/.well-known/genid/{new_uuid}");
Ok(Self(self.0.join(&path).map_err(|e| Error::Url(e))?))
}
}
impl Display for IriRef {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
match self {
Self::Iri(v) => v.fmt(f),
Self::PrefixedName(v) => v.fmt(f),
}
}
}
impl From<Iri> for IriRef {
fn from(value: Iri) -> Self {
Self::Iri(value)
}
}
impl From<&Iri> for IriRef {
fn from(value: &Iri) -> Self {
Self::from(value.clone())
}
}
impl From<PrefixedName> for IriRef {
fn from(value: PrefixedName) -> Self {
Self::PrefixedName(value)
}
}
impl From<&PrefixedName> for IriRef {
fn from(value: &PrefixedName) -> Self {
Self::from(value.clone())
}
}