use std::fmt;
use std::hash::{Hash, Hasher};
use self::iri_rfc3987::{is_absolute_iri_ref, is_relative_iri_ref};
use super::*;
#[derive(Clone, Copy, Debug, Eq)]
pub struct IriData<T: AsRef<str>> {
pub(crate) ns: T,
pub(crate) suffix: Option<T>,
pub(crate) absolute: bool,
}
impl<T> IriData<T>
where
T: AsRef<str>,
{
pub fn len(&self) -> usize {
self.ns.as_ref().len() + self.suffix_borrow().len()
}
pub fn is_empty(&self) -> bool {
self.ns.as_ref().is_empty() && self.suffix_borrow().is_empty()
}
pub fn bytes<'s>(&'s self) -> impl Iterator<Item = u8> + 's {
self.ns.as_ref().bytes().chain(self.suffix_borrow().bytes())
}
pub fn chars<'s>(&'s self) -> impl Iterator<Item = char> + 's {
self.ns.as_ref().chars().chain(self.suffix_borrow().chars())
}
pub fn is_absolute(&self) -> bool {
self.absolute
}
pub(crate) fn new(ns: T, suffix: Option<T>) -> Result<IriData<T>> {
let mut ret = IriData {
ns,
suffix,
absolute: false,
};
let val = ret.to_string();
ret.absolute = is_absolute_iri_ref(&val);
if ret.absolute || is_relative_iri_ref(&val) {
Ok(ret)
} else {
Err(TermError::InvalidIri(val))
}
}
pub(crate) unsafe fn new_unchecked(
ns: T,
suffix: Option<T>,
absolute: Option<bool>,
) -> IriData<T> {
match absolute {
Some(absolute) => IriData {
ns,
suffix,
absolute,
},
None => IriData::new(ns, suffix).unwrap(),
}
}
pub(crate) fn from_with<'a, U, F>(other: &'a IriData<U>, mut factory: F) -> IriData<T>
where
U: AsRef<str>,
F: FnMut(&'a str) -> T,
{
let ns = factory(other.ns.as_ref());
let suffix = match other.suffix {
Some(ref suffix) => Some(factory(suffix.as_ref())),
None => None,
};
IriData {
ns,
suffix,
absolute: other.absolute,
}
}
pub(crate) fn normalized_with<'a, U, F>(
other: &'a IriData<U>,
factory: F,
norm: Normalization,
) -> IriData<T>
where
U: AsRef<str>,
F: FnMut(&str) -> T,
{
match norm {
Normalization::NoSuffix => Self::no_suffix_with(other, factory),
Normalization::LastHashOrSlash => Self::last_hash_or_slash_with(other, factory),
}
}
fn no_suffix_with<'a, U, F>(other: &'a IriData<U>, mut factory: F) -> IriData<T>
where
U: AsRef<str>,
F: FnMut(&str) -> T,
{
let ns = match other.suffix {
Some(_) => factory(&other.to_string()),
None => factory(other.ns.as_ref()),
};
IriData {
ns,
suffix: None,
absolute: other.absolute,
}
}
fn last_hash_or_slash_with<'a, U, F>(other: &'a IriData<U>, mut factory: F) -> IriData<T>
where
U: AsRef<str>,
F: FnMut(&str) -> T,
{
let sep = ['#', '/'];
let ns = other.ns.as_ref();
let absolute = other.absolute;
if let Some(ref suffix) = other.suffix {
let suffix = suffix.as_ref();
if let Some(spos) = suffix.rfind(&sep[..]) {
let mut new_ns = String::with_capacity(ns.len() + spos + 1);
new_ns.push_str(ns);
new_ns.push_str(&suffix[..=spos]);
IriData {
ns: factory(&new_ns),
suffix: Some(factory(&suffix[spos + 1..])),
absolute,
}
} else if let Some(npos) = ns.rfind(&sep[..]) {
let mut new_suffix = String::with_capacity(ns.len() - npos - 1 + suffix.len());
new_suffix.push_str(&ns[npos + 1..]);
new_suffix.push_str(suffix);
IriData {
ns: factory(&ns[..=npos]),
suffix: Some(factory(&new_suffix)),
absolute,
}
} else {
IriData {
ns: factory(&other.to_string()),
suffix: None,
absolute,
}
}
} else if let Some(npos) = ns.rfind(&sep[..]) {
IriData {
ns: factory(&ns[..=npos]),
suffix: Some(factory(&ns[npos + 1..])),
absolute,
}
} else {
IriData {
ns: factory(ns),
suffix: None,
absolute,
}
}
}
fn suffix_borrow(&self) -> &str {
match self.suffix {
Some(ref suffix) => suffix.as_ref(),
None => "",
}
}
}
impl IriData<&'static str> {
pub const unsafe fn from_raw_parts(
ns: &'static str,
suffix: Option<&'static str>,
absolute: bool,
) -> IriData<&'static str> {
IriData {
ns,
suffix,
absolute,
}
}
}
impl<T, U> PartialEq<IriData<U>> for IriData<T>
where
T: AsRef<str>,
U: AsRef<str>,
{
fn eq(&self, other: &IriData<U>) -> bool {
let s_ns = self.ns.as_ref();
let s_sf = self.suffix_borrow();
let o_ns = other.ns.as_ref();
let o_sf = other.suffix_borrow();
(s_ns.len() + s_sf.len()) == (o_ns.len() + o_sf.len()) && {
let mut eq = true;
let it1 = s_ns.chars().chain(s_sf.chars());
let it2 = o_ns.chars().chain(o_sf.chars());
for (c1, c2) in it1.zip(it2) {
if c1 != c2 {
eq = false;
break;
}
}
eq
}
}
}
impl<'a, T> PartialEq<&'a str> for IriData<T>
where
T: AsRef<str>,
{
fn eq(&self, other: &&'a str) -> bool {
let s_ns = self.ns.as_ref();
let s_sf = self.suffix_borrow();
(s_ns.len() + s_sf.len()) == (other.len()) && {
let mut eq = true;
let it1 = s_ns.chars().chain(s_sf.chars());
let it2 = other.chars();
for (c1, c2) in it1.zip(it2) {
if c1 != c2 {
eq = false;
break;
}
}
eq
}
}
}
impl<T, U> PartialEq<Term<U>> for IriData<T>
where
T: AsRef<str>,
U: TermData,
{
#[inline]
fn eq(&self, other: &Term<U>) -> bool {
match other {
Iri(other_iri) => other_iri == self,
_ => false,
}
}
}
impl<T> Hash for IriData<T>
where
T: AsRef<str>,
{
fn hash<H: Hasher>(&self, state: &mut H) {
state.write(self.ns.as_ref().as_bytes());
state.write(self.suffix_borrow().as_bytes());
state.write_u8(0xff);
}
}
impl<T> fmt::Display for IriData<T>
where
T: AsRef<str>,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let ns = self.ns.as_ref();
let suffix = self.suffix_borrow();
write!(f, "{}{}", ns, suffix)
}
}
#[derive(Clone, Copy)]
pub enum Normalization {
NoSuffix,
LastHashOrSlash,
}
impl<'a> IriRefStructure<'a> {
pub fn join_iri<T>(&self, iri_term: &IriData<T>) -> IriData<T>
where
T: AsRef<str> + Clone + From<String>,
{
let parsed_ns = IriRefStructure::new(iri_term.ns.as_ref()).unwrap();
let abs_ns = T::from(self.join(&parsed_ns).to_string());
IriData {
ns: abs_ns,
suffix: iri_term.suffix.clone(),
absolute: true,
}
}
}
#[cfg(test)]
mod test {
}