use crate::object::{InvalidExpandedJson, TryFromJson};
use crate::Term;
use contextual::{AsRefWithContext, DisplayWithContext, WithContext};
use hashbrown::HashMap;
use iref::{Iri, IriBuf};
use json_ld_syntax::IntoJsonWithContextMeta;
use locspan::{Meta, StrippedHash};
use locspan_derive::*;
use rdf_types::{
BlankId, BlankIdBuf, BlankIdVocabulary, InvalidBlankId, IriVocabulary, Vocabulary,
VocabularyMut,
};
use std::convert::TryFrom;
use std::fmt;
use std::hash::Hash;
pub use rdf_types::MetaGenerator as Generator;
pub use rdf_types::Subject as ValidId;
pub type ValidVocabularyId<V> =
ValidId<<V as IriVocabulary>::Iri, <V as BlankIdVocabulary>::BlankId>;
pub type MetaValidVocabularyId<V, M> = Meta<ValidVocabularyId<V>, M>;
pub type VocabularyId<V> = Id<<V as IriVocabulary>::Iri, <V as BlankIdVocabulary>::BlankId>;
pub type MetaVocabularyId<V, M> = Meta<VocabularyId<V>, M>;
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, StrippedPartialEq, StrippedEq)]
#[locspan(stripped(I, B))]
pub enum Id<I = IriBuf, B = BlankIdBuf> {
Valid(ValidId<I, B>),
Invalid(#[locspan(stripped)] String),
}
#[allow(clippy::derive_hash_xor_eq)]
impl<I: Hash, B: Hash> Hash for Id<I, B> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
match self {
Self::Valid(id) => id.hash(state),
Self::Invalid(id) => id.hash(state),
}
}
}
impl<I: PartialEq, B: PartialEq> hashbrown::Equivalent<Id<I, B>> for ValidId<I, B> {
fn equivalent(&self, key: &Id<I, B>) -> bool {
match key {
Id::Valid(id) => self == id,
_ => false,
}
}
}
impl<I: Hash, B: Hash> StrippedHash for Id<I, B> {
fn stripped_hash<H: std::hash::Hasher>(&self, state: &mut H) {
match self {
Self::Valid(id) => id.stripped_hash(state),
Self::Invalid(id) => id.stripped_hash(state),
}
}
}
impl<'a, B> hashbrown::Equivalent<Id<IriBuf, B>> for iref::Iri<'a> {
fn equivalent(&self, key: &Id<IriBuf, B>) -> bool {
match key {
Id::Valid(ValidId::Iri(iri)) => self == iri,
_ => false,
}
}
}
impl<B> hashbrown::Equivalent<Id<IriBuf, B>> for iref::IriBuf {
fn equivalent(&self, key: &Id<IriBuf, B>) -> bool {
match key {
Id::Valid(ValidId::Iri(iri)) => self == iri,
_ => false,
}
}
}
impl<I> hashbrown::Equivalent<Id<I, BlankIdBuf>> for rdf_types::BlankId {
fn equivalent(&self, key: &Id<I, BlankIdBuf>) -> bool {
match key {
Id::Valid(ValidId::Blank(b)) => self == b,
_ => false,
}
}
}
impl<I> hashbrown::Equivalent<Id<I, BlankIdBuf>> for rdf_types::BlankIdBuf {
fn equivalent(&self, key: &Id<I, BlankIdBuf>) -> bool {
match key {
Id::Valid(ValidId::Blank(b)) => self == b,
_ => false,
}
}
}
impl<I, B, M> TryFromJson<I, B, M> for Id<I, B> {
fn try_from_json_in(
vocabulary: &mut impl VocabularyMut<Iri = I, BlankId = B>,
Meta(value, meta): locspan::Meta<json_syntax::Value<M>, M>,
) -> Result<Meta<Self, M>, locspan::Meta<InvalidExpandedJson<M>, M>> {
match value {
json_syntax::Value::String(s) => match Iri::new(s.as_str()) {
Ok(iri) => Ok(Meta(
Self::Valid(ValidId::Iri(vocabulary.insert(iri))),
meta,
)),
Err(_) => match BlankId::new(s.as_str()) {
Ok(blank_id) => Ok(Meta(
Self::Valid(ValidId::Blank(vocabulary.insert_blank_id(blank_id))),
meta,
)),
Err(_) => Ok(Meta(Self::Invalid(s.to_string()), meta)),
},
},
_ => Err(Meta(InvalidExpandedJson::InvalidId, meta)),
}
}
}
impl<I: From<IriBuf>, B: From<BlankIdBuf>> Id<I, B> {
pub fn from_string(s: String) -> Self {
match IriBuf::from_string(s) {
Ok(iri) => Self::Valid(ValidId::Iri(iri.into())),
Err((_, s)) => match BlankIdBuf::new(s) {
Ok(blank) => Self::Valid(ValidId::Blank(blank.into())),
Err(InvalidBlankId(s)) => Self::Invalid(s),
},
}
}
}
impl<I, B> Id<I, B> {
pub fn iri(iri: I) -> Self {
Self::Valid(ValidId::Iri(iri))
}
pub fn blank(b: B) -> Self {
Self::Valid(ValidId::Blank(b))
}
pub fn from_string_in(
vocabulary: &mut impl VocabularyMut<Iri = I, BlankId = B>,
s: String,
) -> Self {
match Iri::new(&s) {
Ok(iri) => Self::Valid(ValidId::Iri(vocabulary.insert(iri))),
Err(_) => match BlankId::new(&s) {
Ok(blank) => Self::Valid(ValidId::Blank(vocabulary.insert_blank_id(blank))),
Err(_) => Self::Invalid(s),
},
}
}
#[inline(always)]
pub fn is_valid(&self) -> bool {
!matches!(self, Self::Invalid(_))
}
pub fn into_blank(self) -> Option<B> {
match self {
Self::Valid(ValidId::Blank(b)) => Some(b),
_ => None,
}
}
#[inline(always)]
pub fn is_blank(&self) -> bool {
matches!(self, Id::Valid(ValidId::Blank(_)))
}
#[inline(always)]
pub fn as_blank(&self) -> Option<&B> {
match self {
Id::Valid(ValidId::Blank(k)) => Some(k),
_ => None,
}
}
#[inline(always)]
pub fn is_iri(&self) -> bool {
matches!(self, Id::Valid(ValidId::Iri(_)))
}
#[inline(always)]
pub fn as_iri(&self) -> Option<&I> {
match self {
Id::Valid(ValidId::Iri(k)) => Some(k),
_ => None,
}
}
#[inline(always)]
pub fn into_term(self) -> Term<I, B> {
Term::Id(self)
}
pub fn as_ref(&self) -> Ref<I, B> {
match self {
Self::Valid(ValidId::Iri(t)) => Ref::Iri(t),
Self::Valid(ValidId::Blank(id)) => Ref::Blank(id),
Self::Invalid(id) => Ref::Invalid(id.as_str()),
}
}
}
impl<I: AsRef<str>, B: AsRef<str>> Id<I, B> {
#[inline(always)]
pub fn as_str(&self) -> &str {
match self {
Id::Valid(ValidId::Iri(id)) => id.as_ref(),
Id::Valid(ValidId::Blank(id)) => id.as_ref(),
Id::Invalid(id) => id.as_str(),
}
}
}
impl<T, B, N: Vocabulary<Iri = T, BlankId = B>> AsRefWithContext<str, N> for Id<T, B> {
fn as_ref_with<'a>(&'a self, vocabulary: &'a N) -> &'a str {
match self {
Id::Valid(ValidId::Iri(id)) => vocabulary.iri(id).unwrap().into_str(),
Id::Valid(ValidId::Blank(id)) => vocabulary.blank_id(id).unwrap().as_str(),
Id::Invalid(id) => id.as_str(),
}
}
}
impl<I: PartialEq, B> PartialEq<I> for Id<I, B> {
fn eq(&self, other: &I) -> bool {
match self {
Id::Valid(ValidId::Iri(id)) => id == other,
_ => false,
}
}
}
impl<T: PartialEq<str>, B: PartialEq<str>> PartialEq<str> for Id<T, B> {
fn eq(&self, other: &str) -> bool {
match self {
Id::Valid(ValidId::Iri(iri)) => iri == other,
Id::Valid(ValidId::Blank(blank)) => blank == other,
Id::Invalid(id) => id == other,
}
}
}
impl<'a, T, B> From<&'a Id<T, B>> for Id<&'a T, &'a B> {
fn from(r: &'a Id<T, B>) -> Id<&'a T, &'a B> {
match r {
Id::Valid(ValidId::Iri(id)) => Id::Valid(ValidId::Iri(id)),
Id::Valid(ValidId::Blank(id)) => Id::Valid(ValidId::Blank(id)),
Id::Invalid(id) => Id::Invalid(id.clone()),
}
}
}
impl<T, B> From<T> for Id<T, B> {
#[inline(always)]
fn from(id: T) -> Id<T, B> {
Id::Valid(ValidId::Iri(id))
}
}
impl<T: PartialEq, B: PartialEq> PartialEq<Term<T, B>> for Id<T, B> {
#[inline]
fn eq(&self, term: &Term<T, B>) -> bool {
match term {
Term::Id(prop) => self == prop,
_ => false,
}
}
}
impl<T: PartialEq, B: PartialEq> PartialEq<Id<T, B>> for Term<T, B> {
#[inline]
fn eq(&self, r: &Id<T, B>) -> bool {
match self {
Term::Id(prop) => prop == r,
_ => false,
}
}
}
impl<T, B> TryFrom<Term<T, B>> for Id<T, B> {
type Error = Term<T, B>;
#[inline]
fn try_from(term: Term<T, B>) -> Result<Id<T, B>, Term<T, B>> {
match term {
Term::Id(prop) => Ok(prop),
term => Err(term),
}
}
}
impl<T: fmt::Display, B: fmt::Display> fmt::Display for Id<T, B> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Id::Valid(id) => id.fmt(f),
Id::Invalid(id) => id.fmt(f),
}
}
}
impl<T, B, N: Vocabulary<Iri = T, BlankId = B>> DisplayWithContext<N> for Id<T, B> {
fn fmt_with(&self, vocabulary: &N, f: &mut fmt::Formatter) -> fmt::Result {
use fmt::Display;
match self {
Id::Valid(id) => id.fmt_with(vocabulary, f),
Id::Invalid(id) => id.fmt(f),
}
}
}
impl<T: fmt::Debug, B: fmt::Debug> fmt::Debug for Id<T, B> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Id::Valid(id) => write!(f, "Id::Valid({:?})", id),
Id::Invalid(id) => write!(f, "Id::Invalid({:?})", id),
}
}
}
impl<T, B, M, N: Vocabulary<Iri = T, BlankId = B>> IntoJsonWithContextMeta<M, N> for Id<T, B> {
fn into_json_meta_with(self, meta: M, context: &N) -> Meta<json_syntax::Value<M>, M> {
Meta(self.into_with(context).to_string().into(), meta)
}
}
impl<T, B> From<ValidId<T, B>> for Id<T, B> {
fn from(r: ValidId<T, B>) -> Self {
Id::Valid(r)
}
}
impl<T, B> TryFrom<Id<T, B>> for ValidId<T, B> {
type Error = String;
fn try_from(r: Id<T, B>) -> Result<Self, Self::Error> {
match r {
Id::Valid(r) => Ok(r),
Id::Invalid(id) => Err(id),
}
}
}
impl<'a, T, B> TryFrom<&'a Id<T, B>> for &'a ValidId<T, B> {
type Error = &'a String;
fn try_from(r: &'a Id<T, B>) -> Result<Self, Self::Error> {
match r {
Id::Valid(r) => Ok(r),
Id::Invalid(id) => Err(id),
}
}
}
impl<'a, T, B> TryFrom<&'a mut Id<T, B>> for &'a mut ValidId<T, B> {
type Error = &'a mut String;
fn try_from(r: &'a mut Id<T, B>) -> Result<Self, Self::Error> {
match r {
Id::Valid(r) => Ok(r),
Id::Invalid(id) => Err(id),
}
}
}
#[derive(Clone, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum Ref<'a, T = IriBuf, B = BlankIdBuf> {
Iri(&'a T),
Blank(&'a B),
Invalid(&'a str),
}
pub trait IdentifyAll<T, B, M> {
fn identify_all_with<N: Vocabulary<Iri = T, BlankId = B>, G: Generator<N, M>>(
&mut self,
vocabulary: &mut N,
generator: &mut G,
) where
M: Clone,
T: Eq + Hash,
B: Eq + Hash;
fn identify_all<G: Generator<(), M>>(&mut self, generator: &mut G)
where
M: Clone,
T: Eq + Hash,
B: Eq + Hash,
(): Vocabulary<Iri = T, BlankId = B>,
{
self.identify_all_with(rdf_types::vocabulary::no_vocabulary_mut(), generator)
}
}
pub trait Relabel<T, B, M> {
fn relabel_with<N: Vocabulary<Iri = T, BlankId = B>, G: Generator<N, M>>(
&mut self,
vocabulary: &mut N,
generator: &mut G,
relabeling: &mut HashMap<B, Meta<ValidId<T, B>, M>>,
) where
M: Clone,
T: Clone + Eq + Hash,
B: Clone + Eq + Hash;
fn relabel<G: Generator<(), M>>(
&mut self,
generator: &mut G,
relabeling: &mut HashMap<B, Meta<ValidId<T, B>, M>>,
) where
M: Clone,
T: Clone + Eq + Hash,
B: Clone + Eq + Hash,
(): Vocabulary<Iri = T, BlankId = B>,
{
self.relabel_with(
rdf_types::vocabulary::no_vocabulary_mut(),
generator,
relabeling,
)
}
}