#![deny(missing_docs)]
use mownstr::MownStr;
use sophia_api::term::{
term_cmp, term_eq, term_format, term_hash, term_to_string, CopyTerm, RawValue, SimpleIri,
TTerm, TermKind, TryCopyTerm,
};
use std::convert::TryInto;
use std::fmt::Debug;
use std::hash::{Hash, Hasher};
use std::rc::Rc;
use std::sync::Arc;
pub mod factory;
pub mod index_map;
pub mod variable;
use self::variable::Variable;
pub mod blank_node;
use self::blank_node::BlankNode;
pub mod iri;
use self::iri::{Iri, Normalization};
pub mod literal;
use literal::convert::{AsLiteral, DataType, NativeLiteral};
use literal::Literal;
mod _display;
mod _error;
pub use self::_error::*;
#[derive(Clone, Copy, Debug, Eq, Ord)]
pub enum Term<TD>
where
TD: TermData,
{
Iri(Iri<TD>),
BNode(BlankNode<TD>),
Literal(Literal<TD>),
Variable(Variable<TD>),
}
pub trait TermData: AsRef<str> + Clone + Eq + Hash {}
impl<T> TermData for T where T: AsRef<str> + Clone + Eq + Hash {}
pub type BoxTerm = Term<Box<str>>;
pub type RcTerm = Term<Rc<str>>;
pub type ArcTerm = Term<Arc<str>>;
pub type RefTerm<'a> = Term<&'a str>;
pub type StaticTerm = RefTerm<'static>;
pub type MownTerm<'a> = Term<MownStr<'a>>;
impl<T> Term<T>
where
T: TermData,
{
pub fn new_iri<U>(iri: U) -> Result<Term<T>>
where
U: AsRef<str>,
T: From<U>,
{
Iri::<T>::new(iri).map(Into::into)
}
pub fn new_iri_suffixed<U, V>(ns: U, suffix: V) -> Result<Term<T>>
where
U: AsRef<str>,
V: AsRef<str>,
T: From<U> + From<V>,
{
Iri::<T>::new_suffixed(ns, suffix).map(Into::into)
}
pub fn new_bnode<U>(id: U) -> Result<Term<T>>
where
U: AsRef<str>,
T: From<U>,
{
BlankNode::new(id).map(Into::into)
}
pub fn new_literal_lang<U, V>(txt: U, lang: V) -> Result<Self>
where
V: AsRef<str>,
T: From<U> + From<V>,
{
Literal::<T>::new_lang(txt, lang).map(Into::into)
}
pub fn new_literal_dt<U, V>(txt: U, dt: V) -> Result<Self>
where
T: From<U>,
V: TryInto<Iri<T>>,
TermError: From<<V as TryInto<Iri<T>>>::Error>,
{
Ok(Literal::new_dt(txt, dt.try_into()?).into())
}
pub fn new_variable<U>(name: U) -> Result<Term<T>>
where
U: AsRef<str>,
T: From<U>,
{
Variable::new(name).map(Into::into)
}
pub fn as_ref(&self) -> Term<&T> {
use self::Term::*;
match &self {
Iri(iri) => Iri(iri.as_ref()),
Literal(lit) => Literal(lit.as_ref()),
BNode(bn) => BNode(bn.as_ref()),
Variable(var) => Variable(var.as_ref()),
}
}
pub fn as_ref_str(&self) -> Term<&str> {
use self::Term::*;
match &self {
Iri(iri) => Iri(iri.as_ref_str()),
Literal(lit) => Literal(lit.as_ref_str()),
BNode(bn) => BNode(bn.as_ref_str()),
Variable(var) => Variable(var.as_ref_str()),
}
}
pub fn map<F, TD2>(self, f: F) -> Term<TD2>
where
F: FnMut(T) -> TD2,
TD2: TermData,
{
use self::Term::*;
match self {
Iri(iri) => Iri(iri.map(f)),
Literal(lit) => Literal(lit.map(f)),
BNode(bn) => BNode(bn.map(f)),
Variable(var) => Variable(var.map(f)),
}
}
pub fn map_into<TD2>(self) -> Term<TD2>
where
T: Into<TD2>,
TD2: TermData,
{
self.map(Into::into)
}
pub fn clone_map<'a, U, F>(&'a self, factory: F) -> Term<U>
where
U: TermData,
F: FnMut(&'a str) -> U,
{
use self::Term::*;
match self {
Iri(iri) => iri.clone_map(factory).into(),
BNode(bn) => bn.clone_map(factory).into(),
Literal(lit) => lit.clone_map(factory).into(),
Variable(var) => var.clone_map(factory).into(),
}
}
pub fn clone_into<'src, U>(&'src self) -> Term<U>
where
U: TermData + From<&'src str>,
{
self.clone_map(Into::into)
}
pub fn normalized(&self, policy: Normalization) -> MownTerm {
match self {
Term::Iri(iri) => iri.normalized(policy).into(),
Term::Literal(lit) => lit.normalized(policy).into(),
_ => self.as_ref_str().map_into(),
}
}
pub fn new_iri_unchecked<U>(iri: U) -> Term<T>
where
T: From<U>,
{
Iri::<T>::new_unchecked(iri).into()
}
pub fn new_iri_suffixed_unchecked<U, V>(ns: U, suffix: V) -> Term<T>
where
T: From<U> + From<V>,
{
Iri::<T>::new_suffixed_unchecked(ns, suffix).into()
}
pub fn new_bnode_unchecked<U>(id: U) -> Term<T>
where
U: AsRef<str>,
T: From<U>,
{
BlankNode::<T>::new_unchecked(id).into()
}
pub fn new_literal_lang_unchecked<U, V>(txt: U, lang: V) -> Self
where
V: AsRef<str>,
T: From<U> + From<V>,
{
Literal::<T>::new_lang_unchecked(txt, lang).into()
}
pub fn new_literal_dt_unchecked<U, V>(txt: U, dt: V) -> Self
where
T: From<U>,
V: TryInto<Iri<T>>,
<V as TryInto<Iri<T>>>::Error: Debug,
{
Literal::new_dt(txt, dt.try_into().unwrap()).into()
}
pub fn new_variable_unchecked<U>(name: U) -> Term<T>
where
U: AsRef<str>,
T: From<U>,
{
Variable::<T>::new_unchecked(name).into()
}
}
impl<T: TermData> TTerm for Term<T> {
fn kind(&self) -> TermKind {
use Term::*;
match self {
Iri(_) => TermKind::Iri,
Literal(_) => TermKind::Literal,
BNode(_) => TermKind::BlankNode,
Variable(_) => TermKind::Variable,
}
}
fn value_raw(&self) -> RawValue {
use Term::*;
match self {
Iri(i) => i.value_raw(),
Literal(l) => l.value_raw(),
BNode(b) => b.value_raw(),
Variable(v) => v.value_raw(),
}
}
fn datatype(&self) -> Option<SimpleIri> {
if let Term::Literal(lit) = self {
lit.datatype()
} else {
None
}
}
fn language(&self) -> Option<&str> {
if let Term::Literal(lit) = self {
lit.language()
} else {
None
}
}
fn as_dyn(&self) -> &dyn TTerm {
self
}
}
impl<TD, TE> PartialEq<TE> for Term<TD>
where
TD: TermData,
TE: TTerm + ?Sized,
{
fn eq(&self, other: &TE) -> bool {
term_eq(self, other)
}
}
impl<TD, TE> PartialOrd<TE> for Term<TD>
where
TD: TermData,
TE: TTerm + ?Sized,
{
fn partial_cmp(&self, other: &TE) -> Option<std::cmp::Ordering> {
Some(term_cmp(self, other))
}
}
impl<TD> Hash for Term<TD>
where
TD: TermData,
{
fn hash<H: Hasher>(&self, state: &mut H) {
term_hash(self, state)
}
}
impl<TD> From<Iri<TD>> for Term<TD>
where
TD: TermData,
{
fn from(iri: Iri<TD>) -> Self {
Term::Iri(iri)
}
}
impl<TD> From<Literal<TD>> for Term<TD>
where
TD: TermData,
{
fn from(lit: Literal<TD>) -> Self {
Term::Literal(lit)
}
}
impl<TD> From<Variable<TD>> for Term<TD>
where
TD: TermData,
{
fn from(var: Variable<TD>) -> Self {
Term::Variable(var)
}
}
impl<TD> From<BlankNode<TD>> for Term<TD>
where
TD: TermData,
{
fn from(bn: BlankNode<TD>) -> Self {
Term::BNode(bn)
}
}
impl<TD> From<String> for Term<TD>
where
TD: TermData + From<Box<str>> + From<&'static str>,
{
fn from(txt: String) -> Self {
txt.as_literal().into()
}
}
impl<'a> From<SimpleIri<'a>> for RefTerm<'a> {
fn from(other: SimpleIri<'a>) -> Self {
Iri::from(other).into()
}
}
impl<T, TD> From<NativeLiteral<T>> for Term<TD>
where
T: DataType + ?Sized,
TD: TermData + From<Box<str>> + From<&'static str>,
{
fn from(other: NativeLiteral<T>) -> Self {
Literal::from(other).into()
}
}
impl<'a, T> From<NativeLiteral<T, &'a str>> for RefTerm<'a>
where
T: DataType + ?Sized,
{
fn from(other: NativeLiteral<T, &'a str>) -> Self {
Literal::from(other).into()
}
}
impl<TD> CopyTerm for Term<TD>
where
TD: TermData + for<'x> From<&'x str>,
{
fn copy<T>(term: &T) -> Self
where
T: TTerm + ?Sized,
{
match term.kind() {
TermKind::Iri => Term::Iri(Iri::try_copy(term).unwrap()),
TermKind::Literal => Term::Literal(Literal::try_copy(term).unwrap()),
TermKind::BlankNode => Term::BNode(BlankNode::try_copy(term).unwrap()),
TermKind::Variable => Term::Variable(Variable::try_copy(term).unwrap()),
}
}
}
impl<'a, T> From<&'a T> for RefTerm<'a>
where
T: TTerm + ?Sized,
{
fn from(t: &'a T) -> Self {
let v = t.value_raw();
match t.kind() {
TermKind::Iri => Term::Iri(match v.1 {
None => Iri::new_unchecked(v.0),
Some(suffix) => Iri::new_suffixed_unchecked(v.0, suffix),
}),
TermKind::Literal => Term::Literal(match t.language() {
None => {
let dt: Iri<&'a str> = t.datatype().unwrap().into();
Literal::new_dt(v.0, dt)
}
Some(tag) => Literal::new_lang_unchecked(v.0, tag),
}),
TermKind::BlankNode => Term::BNode(BlankNode::new_unchecked(v.0)),
TermKind::Variable => Term::Variable(Variable::new_unchecked(v.0)),
}
}
}
#[cfg(test)]
pub(crate) mod test;
pub use sophia_api::term::same_graph_name;
pub mod ns {
pub use sophia_api::ns::*;
}
pub use sophia_api::term::matcher;