use std::convert::TryFrom;
#[cfg(feature = "serde")]
use serde::Serialize;
use validated_slice::{OwnedSliceSpec, SliceSpec};
use crate::{
resolve::resolve_iri,
types::{
iri::set_fragment, AbsoluteIriStr, IriCreationError, IriFragmentStr, IriStr, IriString,
RelativeIriStr, RelativeIriString,
},
validate::iri::{iri as validate_iri, iri_reference, Error},
};
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
#[allow(clippy::derive_hash_xor_eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
pub struct IriReferenceStr(str);
impl IriReferenceStr {
pub fn new(s: &str) -> Result<&Self, Error> {
TryFrom::try_from(s)
}
pub(crate) unsafe fn new_unchecked(s: &str) -> &Self {
debug_assert_eq!(StrSpec::validate(s), Ok(()));
StrSpec::from_inner_unchecked(s)
}
pub fn to_iri(&self) -> Result<&IriStr, &RelativeIriStr> {
<&IriStr>::try_from(self.as_str()).map_err(|_| unsafe {
RelativeIriStr::new_unchecked(self)
})
}
pub fn to_relative_iri(&self) -> Result<&RelativeIriStr, &IriStr> {
match self.to_iri() {
Ok(iri) => Err(iri),
Err(relative) => Ok(relative),
}
}
pub fn resolve_against(&self, base: &AbsoluteIriStr) -> IriString {
resolve_iri(self, base, true)
}
#[deprecated(since = "0.2.2", note = "Renamed to `resolve_against()`")]
pub fn resolve(&self, base: &AbsoluteIriStr) -> IriString {
resolve_iri(self, base, true)
}
pub fn fragment(&self) -> Option<&IriFragmentStr> {
let s: &str = self.as_ref();
s.find('#').map(|colon_pos| unsafe {
IriFragmentStr::new_unchecked(&s[(colon_pos + 1)..])
})
}
pub fn as_str(&self) -> &str {
self.as_ref()
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
pub struct IriReferenceString(String);
impl IriReferenceString {
pub(crate) unsafe fn new_unchecked(s: String) -> Self {
debug_assert_eq!(StrSpec::validate(&s), Ok(()));
StringSpec::from_inner_unchecked(s)
}
pub fn into_iri(self) -> Result<IriString, RelativeIriString> {
let s: String = self.into();
if validate_iri(&s).is_ok() {
Ok(unsafe {
IriString::new_always_unchecked(s)
})
} else {
Err(unsafe {
RelativeIriString::new_unchecked(s)
})
}
}
pub fn into_relative_iri(self) -> Result<RelativeIriString, IriString> {
match self.into_iri() {
Ok(iri) => Err(iri),
Err(relative) => Ok(relative),
}
}
pub fn set_fragment(&mut self, fragment: Option<&IriFragmentStr>) {
set_fragment(&mut self.0, fragment.map(AsRef::as_ref));
debug_assert!(iri_reference(&self.0).is_ok());
}
pub fn shrink_to_fit(&mut self) {
self.0.shrink_to_fit()
}
}
impl_basics! {
Slice {
spec: StrSpec,
custom: IriReferenceStr,
validator: iri_reference,
error: Error,
},
Owned {
spec: StringSpec,
custom: IriReferenceString,
error: IriCreationError<String>,
},
}
validated_slice::impl_std_traits_for_slice! {
Spec {
spec: StrSpec,
custom: IriReferenceStr,
inner: str,
error: Error,
};
{ Deref<Target = {Inner}> };
}
impl_serde! {
expecting: "an IRI reference",
slice: IriReferenceStr,
owned: IriReferenceString,
}