linked-data 0.1.2

Linked-Data dateset serialization/deserialization traits
Documentation
use core::fmt;
use std::borrow::Borrow;

use educe::Educe;
use iref::Iri;
use rdf_types::{
	vocabulary::{
		BlankIdVocabulary, EmbedIntoVocabulary, IriVocabulary, IriVocabularyMut, LiteralVocabulary,
		LiteralVocabularyMut,
	},
	Id, Interpretation, LiteralType, LiteralTypeRef, Quad, Term, Vocabulary,
};

pub type RdfId<V> = Id<<V as IriVocabulary>::Iri, <V as BlankIdVocabulary>::BlankId>;
pub type RdfTerm<V> = Term<RdfId<V>, RdfLiteral<V>>;

pub type RdfIdRef<'a, V> = Id<&'a <V as IriVocabulary>::Iri, &'a <V as BlankIdVocabulary>::BlankId>;
pub type RdfTermRef<'a, V> = Term<RdfIdRef<'a, V>, RdfLiteralRef<'a, V>>;

pub type RdfQuad<V = ()> = Quad<
	RdfId<V>,
	<V as IriVocabulary>::Iri,
	Term<RdfId<V>, <V as LiteralVocabulary>::Literal>,
	RdfId<V>,
>;

pub type InterpretedQuad<I> = Quad<<I as Interpretation>::Resource>;

#[derive(Educe)]
#[educe(Debug(bound = "V::Iri: fmt::Debug, V::BlankId: fmt::Debug"))]
pub enum CowRdfTerm<'a, V: Vocabulary> {
	Borrowed(RdfTermRef<'a, V>),
	Owned(RdfTerm<V>),
}

impl<'a, V: Vocabulary> CowRdfTerm<'a, V> {
	pub fn from_str(vocabulary: &V, value: &'a str, ty: &'a V::Iri) -> Self {
		use xsd_types::ValueRef;

		let ty_iri = vocabulary.iri(ty).unwrap();

		if ty_iri == RDF_JSON {
			use json_syntax::Parse;
			match json_syntax::Value::parse_str(value) {
				Ok((json, _)) => CowRdfTerm::Owned(RdfTerm::Literal(RdfLiteral::Json(json))),
				Err(_) => CowRdfTerm::Borrowed(RdfTermRef::Literal(RdfLiteralRef::Any(
					value,
					LiteralTypeRef::Any(ty),
				))),
			}
		} else {
			match xsd_types::Datatype::from_iri(ty_iri) {
				Some(xsd_types::Datatype::String(xsd_types::StringDatatype::String)) => {
					CowRdfTerm::Borrowed(RdfTermRef::Literal(RdfLiteralRef::Xsd(ValueRef::String(
						value,
					))))
				}
				Some(xsd_ty) => match xsd_ty.parse(value) {
					Ok(xsd_value) => {
						CowRdfTerm::Owned(RdfTerm::Literal(RdfLiteral::Xsd(xsd_value)))
					}
					Err(_) => CowRdfTerm::Borrowed(RdfTermRef::Literal(RdfLiteralRef::Any(
						value,
						LiteralTypeRef::Any(ty),
					))),
				},
				None => CowRdfTerm::Borrowed(RdfTermRef::Literal(RdfLiteralRef::Any(
					value,
					LiteralTypeRef::Any(ty),
				))),
			}
		}
	}
}

pub enum CowRef<'a, T> {
	Borrowed(&'a T),
	Owned(T),
}

impl<'a, T> AsRef<T> for CowRef<'a, T> {
	fn as_ref(&self) -> &T {
		match self {
			Self::Borrowed(t) => t,
			Self::Owned(t) => t,
		}
	}
}

impl<'a, T> Borrow<T> for CowRef<'a, T> {
	fn borrow(&self) -> &T {
		match self {
			Self::Borrowed(t) => t,
			Self::Owned(t) => t,
		}
	}
}

impl<'a, V: Vocabulary> CowRdfTerm<'a, V> {
	#[allow(clippy::type_complexity)]
	pub fn into_term(
		self,
	) -> Term<Id<CowRef<'a, V::Iri>, CowRef<'a, V::BlankId>>, CowRdfLiteral<'a, V>> {
		match self {
			Self::Borrowed(Term::Id(Id::Iri(i))) => Term::Id(Id::Iri(CowRef::Borrowed(i))),
			Self::Borrowed(Term::Id(Id::Blank(b))) => Term::Id(Id::Blank(CowRef::Borrowed(b))),
			Self::Borrowed(Term::Literal(l)) => Term::Literal(CowRdfLiteral::Borrowed(l)),
			Self::Owned(Term::Id(Id::Iri(i))) => Term::Id(Id::Iri(CowRef::Owned(i))),
			Self::Owned(Term::Id(Id::Blank(b))) => Term::Id(Id::Blank(CowRef::Owned(b))),
			Self::Owned(Term::Literal(l)) => Term::Literal(CowRdfLiteral::Owned(l)),
		}
	}

	pub fn as_term_ref(&self) -> RdfTermRef<V> {
		match self {
			Self::Borrowed(t) => *t,
			Self::Owned(t) => match t {
				Term::Id(Id::Iri(i)) => Term::Id(Id::Iri(i)),
				Term::Id(Id::Blank(b)) => Term::Id(Id::Blank(b)),
				Term::Literal(l) => Term::Literal(l.as_literal_ref()),
			},
		}
	}

	pub fn into_owned(self) -> RdfTerm<V>
	where
		V::Iri: Clone,
		V::BlankId: Clone,
	{
		match self {
			Self::Borrowed(t) => match t {
				Term::Id(Id::Iri(i)) => Term::Id(Id::Iri(i.clone())),
				Term::Id(Id::Blank(b)) => Term::Id(Id::Blank(b.clone())),
				Term::Literal(l) => Term::Literal(l.into_owned()),
			},
			Self::Owned(t) => t,
		}
	}
}

pub trait RdfLiteralValue:
	From<String> + From<xsd_types::Value> + From<json_syntax::Value>
{
}

impl<T: From<String> + From<xsd_types::Value> + From<json_syntax::Value>> RdfLiteralValue for T {}

pub trait AsRdfLiteral<V: IriVocabulary + LiteralVocabulary> {
	fn as_rdf_literal<'a>(
		&'a self,
		vocabulary: &V,
		ty: LiteralTypeRef<'a, V::Iri>,
	) -> CowRdfLiteral<V>;
}

impl<V: IriVocabulary + LiteralVocabulary> AsRdfLiteral<V> for str {
	fn as_rdf_literal<'a>(
		&'a self,
		_vocabulary: &V,
		ty: LiteralTypeRef<'a, V::Iri>,
	) -> CowRdfLiteral<V> {
		CowRdfLiteral::Borrowed(RdfLiteralRef::Any(self, ty))
	}
}

impl<V: IriVocabulary + LiteralVocabulary> AsRdfLiteral<V> for String {
	fn as_rdf_literal<'a>(
		&'a self,
		vocabulary: &V,
		ty: LiteralTypeRef<'a, V::Iri>,
	) -> CowRdfLiteral<V> {
		self.as_str().as_rdf_literal(vocabulary, ty)
	}
}

pub trait RdfLiteralType<V: IriVocabulary>: From<rdf_types::LiteralType<V::Iri>> {}

impl<V: IriVocabulary, T: From<rdf_types::LiteralType<V::Iri>>> RdfLiteralType<V> for T {}

#[derive(Educe)]
#[educe(Debug(bound = "V::Iri: fmt::Debug"))]
pub enum RdfLiteral<V: IriVocabulary> {
	Any(String, rdf_types::LiteralType<V::Iri>),
	Xsd(xsd_types::Value),
	Json(json_syntax::Value),
}

impl<V: IriVocabulary> RdfLiteral<V> {
	pub fn into_lexical(self, vocabulary: &V) -> rdf_types::Literal {
		match self {
			Self::Any(s, LiteralType::Any(ty)) => rdf_types::Literal::new(
				s.to_owned(),
				LiteralType::Any(vocabulary.owned_iri(ty).ok().unwrap()),
			),
			Self::Any(s, LiteralType::LangString(lang)) => {
				rdf_types::Literal::new(s.to_owned(), LiteralType::LangString(lang))
			}
			Self::Xsd(value) => {
				let ty = value.datatype().iri().to_owned();
				rdf_types::Literal::new(value.to_string(), LiteralType::Any(ty))
			}
			Self::Json(mut value) => {
				value.canonicalize();
				rdf_types::Literal::new(value.to_string(), LiteralType::Any(RDF_JSON.to_owned()))
			}
		}
	}

	pub fn as_literal_ref(&self) -> RdfLiteralRef<V> {
		match self {
			Self::Any(value, ty) => RdfLiteralRef::Any(value, ty.as_ref()),
			Self::Xsd(value) => RdfLiteralRef::Xsd(value.as_ref()),
			Self::Json(value) => RdfLiteralRef::Json(value),
		}
	}
}

impl<V: IriVocabulary> fmt::Display for RdfLiteral<V> {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		match self {
			Self::Any(v, _) => v.fmt(f),
			Self::Xsd(v) => v.fmt(f),
			Self::Json(v) => v.fmt(f),
		}
	}
}

const RDF_JSON: &Iri = static_iref::iri!("http://www.w3.org/1999/02/22-rdf-syntax-ns#JSON");

impl<V: Vocabulary + IriVocabularyMut + LiteralVocabularyMut> EmbedIntoVocabulary<V>
	for RdfLiteral<V>
{
	type Embedded = V::Literal;

	fn embed_into_vocabulary(self, vocabulary: &mut V) -> V::Literal {
		match self {
			Self::Any(s, ty) => vocabulary.insert_owned_literal(rdf_types::Literal::new(s, ty)),
			Self::Xsd(v) => {
				let ty = rdf_types::LiteralType::Any(vocabulary.insert(v.datatype().iri()));
				vocabulary.insert_owned_literal(rdf_types::Literal::new(v.into(), ty))
			}
			Self::Json(v) => {
				let ty = rdf_types::LiteralType::Any(vocabulary.insert(RDF_JSON));
				vocabulary.insert_owned_literal(rdf_types::Literal::new(v.into(), ty))
			}
		}
	}
}

#[derive(Educe)]
#[educe(Debug(bound = "V::Iri: fmt::Debug"), Clone, Copy)]
pub enum RdfLiteralRef<'a, V: IriVocabulary = ()> {
	Any(&'a str, rdf_types::LiteralTypeRef<'a, V::Iri>),
	Xsd(xsd_types::ValueRef<'a>),
	Json(&'a json_syntax::Value),
}

impl<'a, V: IriVocabulary> RdfLiteralRef<'a, V> {
	pub fn into_lexical(self, vocabulary: &V) -> rdf_types::Literal {
		match self {
			Self::Any(s, LiteralTypeRef::Any(ty)) => rdf_types::Literal::new(
				s.to_owned(),
				LiteralType::Any(vocabulary.iri(ty).unwrap().to_owned()),
			),
			Self::Any(s, LiteralTypeRef::LangString(lang)) => {
				rdf_types::Literal::new(s.to_owned(), LiteralType::LangString(lang.to_owned()))
			}
			Self::Xsd(value) => {
				let ty = value.datatype().iri().to_owned();
				rdf_types::Literal::new(value.to_string(), LiteralType::Any(ty))
			}
			Self::Json(value) => {
				let mut value = value.clone();
				value.canonicalize();
				rdf_types::Literal::new(value.to_string(), LiteralType::Any(RDF_JSON.to_owned()))
			}
		}
	}

	pub fn into_owned(self) -> RdfLiteral<V>
	where
		V::Iri: Clone,
	{
		match self {
			Self::Any(value, ty) => RdfLiteral::Any(value.to_owned(), ty.into_owned()),
			Self::Xsd(value) => RdfLiteral::Xsd(value.into_owned()),
			Self::Json(value) => RdfLiteral::Json(value.clone()),
		}
	}
}

pub enum CowRdfLiteral<'a, V: IriVocabulary = ()> {
	Borrowed(RdfLiteralRef<'a, V>),
	Owned(RdfLiteral<V>),
}

impl<'a, V: IriVocabulary> CowRdfLiteral<'a, V> {
	pub fn into_owned(self) -> RdfLiteral<V>
	where
		V::Iri: Clone,
	{
		match self {
			Self::Borrowed(l) => l.into_owned(),
			Self::Owned(l) => l,
		}
	}

	pub fn as_literal_ref(&self) -> RdfLiteralRef<V> {
		match self {
			Self::Borrowed(l) => *l,
			Self::Owned(l) => l.as_literal_ref(),
		}
	}
}