diff --git a/lib/Cargo.toml b/lib/Cargo.toml index dd65c81d..5dd75348 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -42,7 +42,7 @@ lazy_static = "1" sophia_api = { version = "0.7", optional = true } json-event-parser = "0.1" num_cpus = "1" -oxrdf = { version = "0.1", path="oxrdf" } +oxrdf = { version = "0.1", path="oxrdf", features = ["rdf-star"] } spargebra = { version = "0.1", path="../spargebra", features = ["rdf-star"] } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/lib/oxrdf/Cargo.toml b/lib/oxrdf/Cargo.toml index c9a5834b..d3d86f31 100644 --- a/lib/oxrdf/Cargo.toml +++ b/lib/oxrdf/Cargo.toml @@ -12,6 +12,10 @@ A library providing basic data structures related to RDF """ edition = "2021" +[features] +default = [] +rdf-star = [] + [dependencies] rand = "0.8" oxilangtag = "0.1" diff --git a/lib/oxrdf/README.md b/lib/oxrdf/README.md index fd16c0e7..46065a85 100644 --- a/lib/oxrdf/README.md +++ b/lib/oxrdf/README.md @@ -11,6 +11,8 @@ OxRDF is a simple library providing datastructures encoding [RDF 1.1 concepts](h This crate is intended to be a basic building block of other crates like [Oxigraph](https://crates.io/crates/oxigraph) or [Spargebra](https://crates.io/crates/spargebra). +Support for [RDF-star](https://w3c.github.io/rdf-star/cg-spec/) is available behind the `rdf-star` feature. + Inspired by [RDF/JS](https://rdf.js.org/data-model-spec/) and [Apache Commons RDF](http://commons.apache.org/proper/commons-rdf/). Usage example: diff --git a/lib/oxrdf/src/dataset.rs b/lib/oxrdf/src/dataset.rs index 5562420d..ad2d7079 100644 --- a/lib/oxrdf/src/dataset.rs +++ b/lib/oxrdf/src/dataset.rs @@ -536,12 +536,16 @@ impl Dataset { for (g, s, _, o) in &self.gspo { if let InternedSubject::BlankNode(bnode) = s { bnodes.insert(*bnode); - } else if let InternedSubject::Triple(triple) = s { + } + #[cfg(feature = "rdf-star")] + if let InternedSubject::Triple(triple) = s { self.triple_blank_nodes(triple, &mut bnodes); } if let InternedTerm::BlankNode(bnode) = o { bnodes.insert(*bnode); - } else if let InternedTerm::Triple(triple) = o { + } + #[cfg(feature = "rdf-star")] + if let InternedTerm::Triple(triple) = o { self.triple_blank_nodes(triple, &mut bnodes); } if let InternedGraphName::BlankNode(bnode) = g { @@ -551,6 +555,7 @@ impl Dataset { bnodes } + #[cfg(feature = "rdf-star")] fn triple_blank_nodes(&self, triple: &InternedTriple, bnodes: &mut HashSet) { if let InternedSubject::BlankNode(bnode) = &triple.subject { bnodes.insert(*bnode); @@ -633,20 +638,24 @@ impl Dataset { node: &InternedSubject, bnodes_hash: &HashMap, ) -> u64 { + #[cfg(feature = "rdf-star")] + if let InternedSubject::Triple(triple) = node { + return self.hash_triple(triple, bnodes_hash); + } if let InternedSubject::BlankNode(bnode) = node { bnodes_hash[bnode] - } else if let InternedSubject::Triple(triple) = node { - self.hash_triple(triple, bnodes_hash) } else { Self::hash_tuple(node.decode_from(&self.interner)) } } fn hash_term(&self, term: &InternedTerm, bnodes_hash: &HashMap) -> u64 { + #[cfg(feature = "rdf-star")] + if let InternedTerm::Triple(triple) = term { + return self.hash_triple(triple, bnodes_hash); + } if let InternedTerm::BlankNode(bnode) = term { bnodes_hash[bnode] - } else if let InternedTerm::Triple(triple) = term { - self.hash_triple(triple, bnodes_hash) } else { Self::hash_tuple(term.decode_from(&self.interner)) } @@ -664,6 +673,7 @@ impl Dataset { } } + #[cfg(feature = "rdf-star")] fn hash_triple( &self, triple: &InternedTriple, @@ -738,24 +748,42 @@ impl Dataset { ( if let InternedSubject::BlankNode(bnode) = s { InternedSubject::BlankNode(self.map_bnode(bnode, hashes)) - } else if let InternedSubject::Triple(triple) = s { - InternedSubject::Triple(Box::new(InternedTriple::encoded_into( - self.label_triple(&triple, hashes).as_ref(), - &mut self.interner, - ))) } else { - s + #[cfg(feature = "rdf-star")] + { + if let InternedSubject::Triple(triple) = s { + InternedSubject::Triple(Box::new(InternedTriple::encoded_into( + self.label_triple(&triple, hashes).as_ref(), + &mut self.interner, + ))) + } else { + s + } + } + #[cfg(not(feature = "rdf-star"))] + { + s + } }, p, if let InternedTerm::BlankNode(bnode) = o { InternedTerm::BlankNode(self.map_bnode(bnode, hashes)) - } else if let InternedTerm::Triple(triple) = o { - InternedTerm::Triple(Box::new(InternedTriple::encoded_into( - self.label_triple(&triple, hashes).as_ref(), - &mut self.interner, - ))) } else { - o + #[cfg(feature = "rdf-star")] + { + if let InternedTerm::Triple(triple) = o { + InternedTerm::Triple(Box::new(InternedTriple::encoded_into( + self.label_triple(&triple, hashes).as_ref(), + &mut self.interner, + ))) + } else { + o + } + } + #[cfg(not(feature = "rdf-star"))] + { + o + } }, if let InternedGraphName::BlankNode(bnode) = g { InternedGraphName::BlankNode(self.map_bnode(bnode, hashes)) @@ -769,6 +797,7 @@ impl Dataset { quads } + #[cfg(feature = "rdf-star")] fn label_triple( &mut self, triple: &InternedTriple, diff --git a/lib/oxrdf/src/interning.rs b/lib/oxrdf/src/interning.rs index 434182d2..22e84547 100644 --- a/lib/oxrdf/src/interning.rs +++ b/lib/oxrdf/src/interning.rs @@ -7,6 +7,7 @@ use std::collections::HashMap; #[derive(Debug, Default)] pub struct Interner { strings: Rodeo, + #[cfg(feature = "rdf-star")] triples: HashMap, } @@ -175,6 +176,7 @@ impl InternedLiteral { pub enum InternedSubject { NamedNode(InternedNamedNode), BlankNode(InternedBlankNode), + #[cfg(feature = "rdf-star")] Triple(Box), } @@ -187,6 +189,7 @@ impl InternedSubject { SubjectRef::BlankNode(node) => { Self::BlankNode(InternedBlankNode::encoded_into(node, interner)) } + #[cfg(feature = "rdf-star")] SubjectRef::Triple(triple) => Self::Triple(Box::new(InternedTriple::encoded_into( triple.as_ref(), interner, @@ -202,6 +205,7 @@ impl InternedSubject { SubjectRef::BlankNode(node) => { Self::BlankNode(InternedBlankNode::encoded_from(node, interner)?) } + #[cfg(feature = "rdf-star")] SubjectRef::Triple(triple) => Self::Triple(Box::new(InternedTriple::encoded_from( triple.as_ref(), interner, @@ -213,6 +217,7 @@ impl InternedSubject { match self { Self::NamedNode(node) => SubjectRef::NamedNode(node.decode_from(interner)), Self::BlankNode(node) => SubjectRef::BlankNode(node.decode_from(interner)), + #[cfg(feature = "rdf-star")] Self::Triple(triple) => SubjectRef::Triple(&interner.triples[triple.as_ref()]), } } @@ -225,6 +230,7 @@ impl InternedSubject { match self { Self::NamedNode(node) => Self::NamedNode(node.next()), Self::BlankNode(node) => Self::BlankNode(node.next()), + #[cfg(feature = "rdf-star")] Self::Triple(triple) => Self::Triple(Box::new(triple.next())), } } @@ -296,6 +302,7 @@ pub enum InternedTerm { NamedNode(InternedNamedNode), BlankNode(InternedBlankNode), Literal(InternedLiteral), + #[cfg(feature = "rdf-star")] Triple(Box), } @@ -309,6 +316,7 @@ impl InternedTerm { Self::BlankNode(InternedBlankNode::encoded_into(term, interner)) } TermRef::Literal(term) => Self::Literal(InternedLiteral::encoded_into(term, interner)), + #[cfg(feature = "rdf-star")] TermRef::Triple(triple) => Self::Triple(Box::new(InternedTriple::encoded_into( triple.as_ref(), interner, @@ -325,6 +333,7 @@ impl InternedTerm { Self::BlankNode(InternedBlankNode::encoded_from(term, interner)?) } TermRef::Literal(term) => Self::Literal(InternedLiteral::encoded_from(term, interner)?), + #[cfg(feature = "rdf-star")] TermRef::Triple(triple) => Self::Triple(Box::new(InternedTriple::encoded_from( triple.as_ref(), interner, @@ -337,6 +346,7 @@ impl InternedTerm { Self::NamedNode(term) => TermRef::NamedNode(term.decode_from(interner)), Self::BlankNode(term) => TermRef::BlankNode(term.decode_from(interner)), Self::Literal(term) => TermRef::Literal(term.decode_from(interner)), + #[cfg(feature = "rdf-star")] Self::Triple(triple) => TermRef::Triple(&interner.triples[triple.as_ref()]), } } @@ -350,6 +360,7 @@ impl InternedTerm { Self::NamedNode(node) => Self::NamedNode(node.next()), Self::BlankNode(node) => Self::BlankNode(node.next()), Self::Literal(node) => Self::Literal(node.next()), + #[cfg(feature = "rdf-star")] Self::Triple(triple) => Self::Triple(Box::new(triple.next())), } } @@ -366,6 +377,7 @@ pub struct InternedTriple { pub object: InternedTerm, } +#[cfg(feature = "rdf-star")] impl InternedTriple { pub fn encoded_into(triple: TripleRef<'_>, interner: &mut Interner) -> Self { let interned_triple = Self { diff --git a/lib/oxrdf/src/lib.rs b/lib/oxrdf/src/lib.rs index c33d50b1..11933965 100644 --- a/lib/oxrdf/src/lib.rs +++ b/lib/oxrdf/src/lib.rs @@ -2,6 +2,8 @@ //! //! This crate is intended to be a basic building block of other crates like [Oxigraph](https://crates.io/crates/oxigraph) or [Spargebra](https://crates.io/crates/spargebra). //! +//! Support for [RDF-star](https://w3c.github.io/rdf-star/cg-spec/) is available behind the `rdf-star` feature. +//! //! Inspired by [RDF/JS](https://rdf.js.org/data-model-spec/) and [Apache Commons RDF](http://commons.apache.org/proper/commons-rdf/). //! //! Usage example: diff --git a/lib/oxrdf/src/parser.rs b/lib/oxrdf/src/parser.rs index 51b86d21..26c7459e 100644 --- a/lib/oxrdf/src/parser.rs +++ b/lib/oxrdf/src/parser.rs @@ -301,24 +301,31 @@ fn read_term(s: &str) -> Result<(Term, &str), TermParseError> { let (object, remain) = read_term(remain)?; let remain = remain.trim_start(); if let Some(remain) = remain.strip_prefix(">>") { - Ok(( - Triple { - subject: match subject { - Term::NamedNode(s) => s.into(), - Term::BlankNode(s) => s.into(), - Term::Literal(_) => { - return Err(TermParseError::msg( - "Literals are not allowed in subject position", - )) - } - Term::Triple(s) => Subject::Triple(s), - }, - predicate, - object, - } - .into(), - remain, - )) + #[cfg(feature = "rdf-star")] + { + Ok(( + Triple { + subject: match subject { + Term::NamedNode(s) => s.into(), + Term::BlankNode(s) => s.into(), + Term::Literal(_) => { + return Err(TermParseError::msg( + "Literals are not allowed in subject position", + )) + } + Term::Triple(s) => Subject::Triple(s), + }, + predicate, + object, + } + .into(), + remain, + )) + } + #[cfg(not(feature = "rdf-star"))] + { + Err(TermParseError::msg("RDF-star is not supported")) + } } else { Err(TermParseError::msg( "Nested triple serialization should be enclosed between << and >>", diff --git a/lib/oxrdf/src/triple.rs b/lib/oxrdf/src/triple.rs index 58727a38..ebcbf846 100644 --- a/lib/oxrdf/src/triple.rs +++ b/lib/oxrdf/src/triple.rs @@ -157,6 +157,7 @@ impl<'a> From> for NamedOrBlankNode { pub enum Subject { NamedNode(NamedNode), BlankNode(BlankNode), + #[cfg(feature = "rdf-star")] Triple(Arc), } @@ -171,6 +172,7 @@ impl Subject { self.as_ref().is_blank_node() } + #[cfg(feature = "rdf-star")] #[inline] pub fn is_triple(&self) -> bool { self.as_ref().is_triple() @@ -181,6 +183,7 @@ impl Subject { match self { Self::NamedNode(node) => SubjectRef::NamedNode(node.as_ref()), Self::BlankNode(node) => SubjectRef::BlankNode(node.as_ref()), + #[cfg(feature = "rdf-star")] Self::Triple(triple) => SubjectRef::Triple(triple), } } @@ -221,6 +224,7 @@ impl From> for Subject { } } +#[cfg(feature = "rdf-star")] impl From for Subject { #[inline] fn from(node: Triple) -> Self { @@ -228,6 +232,7 @@ impl From for Subject { } } +#[cfg(feature = "rdf-star")] impl From> for Subject { #[inline] fn from(node: Arc) -> Self { @@ -235,6 +240,7 @@ impl From> for Subject { } } +#[cfg(feature = "rdf-star")] impl From> for Subject { #[inline] fn from(node: Box) -> Self { @@ -242,6 +248,7 @@ impl From> for Subject { } } +#[cfg(feature = "rdf-star")] impl From> for Subject { #[inline] fn from(node: TripleRef<'_>) -> Self { @@ -271,6 +278,7 @@ impl From> for Subject { pub enum SubjectRef<'a> { NamedNode(NamedNodeRef<'a>), BlankNode(BlankNodeRef<'a>), + #[cfg(feature = "rdf-star")] Triple(&'a Triple), } @@ -285,6 +293,7 @@ impl<'a> SubjectRef<'a> { matches!(self, Self::BlankNode(_)) } + #[cfg(feature = "rdf-star")] #[inline] pub fn is_triple(&self) -> bool { matches!(self, Self::Triple(_)) @@ -295,6 +304,7 @@ impl<'a> SubjectRef<'a> { match self { Self::NamedNode(node) => Subject::NamedNode(node.into_owned()), Self::BlankNode(node) => Subject::BlankNode(node.into_owned()), + #[cfg(feature = "rdf-star")] Self::Triple(triple) => Subject::Triple(Arc::new(triple.clone())), } } @@ -306,6 +316,7 @@ impl fmt::Display for SubjectRef<'_> { match self { Self::NamedNode(node) => node.fmt(f), Self::BlankNode(node) => node.fmt(f), + #[cfg(feature = "rdf-star")] Self::Triple(triple) => write!(f, "<<{}>>", triple), } } @@ -339,6 +350,7 @@ impl<'a> From<&'a BlankNode> for SubjectRef<'a> { } } +#[cfg(feature = "rdf-star")] impl<'a> From<&'a Triple> for SubjectRef<'a> { #[inline] fn from(node: &'a Triple) -> Self { @@ -384,6 +396,7 @@ pub enum Term { NamedNode(NamedNode), BlankNode(BlankNode), Literal(Literal), + #[cfg(feature = "rdf-star")] Triple(Arc), } @@ -403,6 +416,7 @@ impl Term { self.as_ref().is_literal() } + #[cfg(feature = "rdf-star")] #[inline] pub fn is_triple(&self) -> bool { self.as_ref().is_triple() @@ -414,6 +428,7 @@ impl Term { Self::NamedNode(node) => TermRef::NamedNode(node.as_ref()), Self::BlankNode(node) => TermRef::BlankNode(node.as_ref()), Self::Literal(literal) => TermRef::Literal(literal.as_ref()), + #[cfg(feature = "rdf-star")] Self::Triple(triple) => TermRef::Triple(triple), } } @@ -468,6 +483,7 @@ impl From> for Term { } } +#[cfg(feature = "rdf-star")] impl From for Term { #[inline] fn from(triple: Triple) -> Self { @@ -475,6 +491,7 @@ impl From for Term { } } +#[cfg(feature = "rdf-star")] impl From> for Term { #[inline] fn from(node: Arc) -> Self { @@ -482,6 +499,7 @@ impl From> for Term { } } +#[cfg(feature = "rdf-star")] impl From> for Term { #[inline] fn from(node: Box) -> Self { @@ -489,6 +507,7 @@ impl From> for Term { } } +#[cfg(feature = "rdf-star")] impl From> for Term { #[inline] fn from(triple: TripleRef<'_>) -> Self { @@ -519,6 +538,7 @@ impl From for Term { match node { Subject::NamedNode(node) => node.into(), Subject::BlankNode(node) => node.into(), + #[cfg(feature = "rdf-star")] Subject::Triple(triple) => Self::Triple(triple), } } @@ -538,6 +558,7 @@ pub enum TermRef<'a> { NamedNode(NamedNodeRef<'a>), BlankNode(BlankNodeRef<'a>), Literal(LiteralRef<'a>), + #[cfg(feature = "rdf-star")] Triple(&'a Triple), } @@ -557,6 +578,7 @@ impl<'a> TermRef<'a> { matches!(self, Self::Literal(_)) } + #[cfg(feature = "rdf-star")] #[inline] pub fn is_triple(&self) -> bool { matches!(self, Self::Triple(_)) @@ -568,6 +590,7 @@ impl<'a> TermRef<'a> { Self::NamedNode(node) => Term::NamedNode(node.into_owned()), Self::BlankNode(node) => Term::BlankNode(node.into_owned()), Self::Literal(literal) => Term::Literal(literal.into_owned()), + #[cfg(feature = "rdf-star")] Self::Triple(triple) => Term::Triple(Arc::new(triple.clone())), } } @@ -580,6 +603,7 @@ impl fmt::Display for TermRef<'_> { Self::NamedNode(node) => node.fmt(f), Self::BlankNode(node) => node.fmt(f), Self::Literal(literal) => literal.fmt(f), + #[cfg(feature = "rdf-star")] Self::Triple(triple) => { write!(f, "<<{}>>", triple) } @@ -629,6 +653,7 @@ impl<'a> From<&'a Literal> for TermRef<'a> { } } +#[cfg(feature = "rdf-star")] impl<'a> From<&'a Triple> for TermRef<'a> { #[inline] fn from(node: &'a Triple) -> Self { @@ -659,6 +684,7 @@ impl<'a> From> for TermRef<'a> { match node { SubjectRef::NamedNode(node) => node.into(), SubjectRef::BlankNode(node) => node.into(), + #[cfg(feature = "rdf-star")] SubjectRef::Triple(triple) => triple.into(), } }