diff --git a/Cargo.lock b/Cargo.lock index f7a00f04..acfe722f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -845,6 +845,7 @@ dependencies = [ "oxhttp", "oxilangtag", "oxiri", + "oxrdf", "oxrocksdb-sys", "quick-xml", "rand", @@ -908,6 +909,18 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8e54fb37480ffa01a9af93f991ba229b3091f2e6ac84c8f6df2c509356cc473" +[[package]] +name = "oxrdf" +version = "0.1.0" +dependencies = [ + "lasso", + "oxilangtag", + "oxiri", + "rand", + "rio_api", + "sophia_api", +] + [[package]] name = "oxrocksdb-sys" version = "0.3.0-dev" diff --git a/Cargo.toml b/Cargo.toml index 0fb3a213..ecce2e44 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ members = [ "js", "lib", + "lib/oxrdf", "python", "rocksdb-sys", "server", diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 3edd7b12..dd65c81d 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -18,7 +18,7 @@ all-features = true [features] default = [] -sophia = ["sophia_api"] +sophia = ["sophia_api", "oxrdf/sophia_api"] http_client = ["oxhttp", "oxhttp/rustls"] [dependencies] @@ -42,6 +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" } 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 new file mode 100644 index 00000000..c9a5834b --- /dev/null +++ b/lib/oxrdf/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "oxrdf" +version = "0.1.0" +authors = ["Tpt "] +license = "MIT OR Apache-2.0" +readme = "README.md" +keywords = ["RDF"] +repository = "https://github.com/oxigraph/oxigraph/tree/master/lib/oxrdf" +homepage = "https://oxigraph.org/" +description = """ +A library providing basic data structures related to RDF +""" +edition = "2021" + +[dependencies] +rand = "0.8" +oxilangtag = "0.1" +oxiri = "0.1" +rio_api = "0.6" +lasso = {version="0.6", features=["multi-threaded", "inline-more"]} +sophia_api = { version = "0.7", optional = true } + +[package.metadata.docs.rs] +all-features = true diff --git a/lib/oxrdf/README.md b/lib/oxrdf/README.md new file mode 100644 index 00000000..fd16c0e7 --- /dev/null +++ b/lib/oxrdf/README.md @@ -0,0 +1,47 @@ +OxRDF +===== + +[![Latest Version](https://img.shields.io/crates/v/oxrdf.svg)](https://crates.io/crates/oxrdf) +[![Released API docs](https://docs.rs/oxrdf/badge.svg)](https://docs.rs/oxrdf) +[![Crates.io downloads](https://img.shields.io/crates/d/oxrdf)](https://crates.io/crates/oxrdf) +[![actions status](https://github.com/oxigraph/oxigraph/workflows/build/badge.svg)](https://github.com/oxigraph/oxigraph/actions) +[![Gitter](https://badges.gitter.im/oxigraph/community.svg)](https://gitter.im/oxigraph/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + +OxRDF is a simple library providing datastructures encoding [RDF 1.1 concepts](https://www.w3.org/TR/rdf11-concepts/). + +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). + +Inspired by [RDF/JS](https://rdf.js.org/data-model-spec/) and [Apache Commons RDF](http://commons.apache.org/proper/commons-rdf/). + +Usage example: + +```rust +use oxrdf::*; + +let mut graph = Graph::default(); + +// insertion +let ex = NamedNodeRef::new("http://example.com")?; +let triple = TripleRef::new(ex, ex, ex); +graph.insert(triple); + +// simple filter +let results: Vec<_> = graph.triples_for_subject(ex).collect(); +assert_eq!(vec![triple], results); +``` + +## License + +This project is licensed under either of + +* Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) +* MIT license ([LICENSE-MIT](../LICENSE-MIT) or + http://opensource.org/licenses/MIT) + +at your option. + + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in Futures by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. diff --git a/lib/src/model/blank_node.rs b/lib/oxrdf/src/blank_node.rs similarity index 92% rename from lib/src/model/blank_node.rs rename to lib/oxrdf/src/blank_node.rs index 8929f3ab..08f9de57 100644 --- a/lib/src/model/blank_node.rs +++ b/lib/oxrdf/src/blank_node.rs @@ -14,13 +14,13 @@ use std::str; /// /// The default string formatter is returning an N-Triples, Turtle and SPARQL compatible representation: /// ``` -/// use oxigraph::model::BlankNode; +/// use oxrdf::BlankNode; /// /// assert_eq!( /// "_:a122", /// BlankNode::new("a122")?.to_string() /// ); -/// # Result::<_,oxigraph::model::BlankNodeIdParseError>::Ok(()) +/// # Result::<_,oxrdf::BlankNodeIdParseError>::Ok(()) /// ``` #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub struct BlankNode(BlankNodeContent); @@ -62,8 +62,8 @@ impl BlankNode { /// Creates a blank node from a unique numerical id /// /// In most cases, it is much more convenient to create a blank node using [`BlankNode::default()`]. - pub fn new_from_unique_id(id: impl Into) -> Self { - let id = id.into(); + #[inline] + pub fn new_from_unique_id(id: u128) -> Self { Self(BlankNodeContent::Anonymous { id, str: IdStr::new(id), @@ -124,13 +124,13 @@ impl Default for BlankNode { /// /// The default string formatter is returning an N-Triples, Turtle and SPARQL compatible representation: /// ``` -/// use oxigraph::model::BlankNodeRef; +/// use oxrdf::BlankNodeRef; /// /// assert_eq!( /// "_:a122", /// BlankNodeRef::new("a122")?.to_string() /// ); -/// # Result::<_,oxigraph::model::BlankNodeIdParseError>::Ok(()) +/// # Result::<_,oxrdf::BlankNodeIdParseError>::Ok(()) /// ``` #[derive(Eq, PartialEq, Debug, Clone, Copy, Hash)] pub struct BlankNodeRef<'a>(BlankNodeRefContent<'a>); @@ -179,9 +179,17 @@ impl<'a> BlankNodeRef<'a> { } } - /// Returns the internal numerical ID of this blank node, if it exists + /// Returns the internal numerical ID of this blank node if it has been created using [`BlankNode::new_from_unique_id`] + /// + /// ``` + /// use oxrdf::BlankNode; + /// + /// assert_eq!(BlankNode::new_from_unique_id(128).as_ref().unique_id(), Some(128)); + /// assert_eq!(BlankNode::new("128")?.as_ref().unique_id(), None); + /// # Result::<_,oxrdf::BlankNodeIdParseError>::Ok(()) + /// ``` #[inline] - pub(crate) fn id(&self) -> Option { + pub fn unique_id(&self) -> Option { match self.0 { BlankNodeRefContent::Named(_) => None, BlankNodeRefContent::Anonymous { id, .. } => Some(id), @@ -349,13 +357,13 @@ mod tests { #[test] fn as_str_partial() { - let b = BlankNode::new_from_unique_id(0x42_u128); + let b = BlankNode::new_from_unique_id(0x42); assert_eq!(b.as_str(), "42"); } #[test] fn as_str_full() { - let b = BlankNode::new_from_unique_id(0x7777_6666_5555_4444_3333_2222_1111_0000_u128); + let b = BlankNode::new_from_unique_id(0x7777_6666_5555_4444_3333_2222_1111_0000); assert_eq!(b.as_str(), "77776666555544443333222211110000"); } @@ -374,11 +382,11 @@ mod tests { fn new_numerical() { assert_eq!( BlankNode::new("100a").unwrap(), - BlankNode::new_from_unique_id(0x100a_u128), + BlankNode::new_from_unique_id(0x100a), ); assert_ne!( BlankNode::new("100A").unwrap(), - BlankNode::new_from_unique_id(0x100a_u128) + BlankNode::new_from_unique_id(0x100a) ); } diff --git a/lib/src/model/dataset.rs b/lib/oxrdf/src/dataset.rs similarity index 98% rename from lib/src/model/dataset.rs rename to lib/oxrdf/src/dataset.rs index da2e39ce..5562420d 100644 --- a/lib/src/model/dataset.rs +++ b/lib/oxrdf/src/dataset.rs @@ -2,7 +2,7 @@ //! //! Usage example: //! ``` -//! use oxigraph::model::*; +//! use oxrdf::*; //! //! let mut dataset = Dataset::default(); //! @@ -23,9 +23,9 @@ //! //! See also [`Graph`](super::Graph) if you only care about plain triples. -use crate::model::interning::*; -use crate::model::SubjectRef; -use crate::model::*; +use crate::interning::*; +use crate::SubjectRef; +use crate::*; use std::collections::hash_map::DefaultHasher; use std::collections::BTreeSet; use std::collections::{HashMap, HashSet}; @@ -40,7 +40,7 @@ use std::hash::{Hash, Hasher}; /// /// Usage example: /// ``` -/// use oxigraph::model::*; +/// use oxrdf::*; /// /// let mut dataset = Dataset::default(); /// @@ -108,7 +108,7 @@ impl Dataset { /// Provides a read-only view on a [RDF graph](https://www.w3.org/TR/rdf11-concepts/#dfn-graph) contained in this dataset. /// /// ``` - /// use oxigraph::model::*; + /// use oxrdf::*; /// /// let mut dataset = Dataset::default(); /// let ex = NamedNodeRef::new("http://example.com")?; @@ -131,7 +131,7 @@ impl Dataset { /// Provides a read/write view on a [RDF graph](https://www.w3.org/TR/rdf11-concepts/#dfn-graph) contained in this dataset. /// /// ``` - /// use oxigraph::model::*; + /// use oxrdf::*; /// /// let mut dataset = Dataset::default(); /// let ex = NamedNodeRef::new("http://example.com")?; @@ -491,7 +491,7 @@ impl Dataset { /// /// Usage example ([Dataset isomorphim](https://www.w3.org/TR/rdf11-concepts/#dfn-dataset-isomorphism)): /// ``` - /// use oxigraph::model::*; + /// use oxrdf::*; /// /// let iri = NamedNodeRef::new("http://example.com")?; /// @@ -504,8 +504,8 @@ impl Dataset { /// let mut graph2 = Graph::new(); /// let bnode2 = BlankNode::default(); /// let g2 = BlankNode::default(); - /// graph1.insert(QuadRef::new(iri, iri, &bnode2, &g2)); - /// graph1.insert(QuadRef::new(&bnode2, iri, iri, &g2)); + /// graph2.insert(QuadRef::new(iri, iri, &bnode2, &g2)); + /// graph2.insert(QuadRef::new(&bnode2, iri, iri, &g2)); /// /// assert_ne!(graph1, graph2); /// graph1.canonicalize(); @@ -808,7 +808,7 @@ impl Dataset { old_bnode: InternedBlankNode, hashes: &HashMap, ) -> BlankNode { - BlankNode::new_from_unique_id(hashes[&old_bnode]) + BlankNode::new_from_unique_id(hashes[&old_bnode].into()) } } @@ -884,7 +884,7 @@ impl fmt::Display for Dataset { /// /// Usage example: /// ``` -/// use oxigraph::model::*; +/// use oxrdf::*; /// /// let mut dataset = Dataset::default(); /// let ex = NamedNodeRef::new("http://example.com")?; @@ -1219,7 +1219,7 @@ impl<'a> fmt::Display for GraphView<'a> { /// /// Usage example: /// ``` -/// use oxigraph::model::*; +/// use oxrdf::*; /// /// let mut dataset = Dataset::default(); /// let ex = NamedNodeRef::new("http://example.com")?; diff --git a/lib/src/model/graph.rs b/lib/oxrdf/src/graph.rs similarity index 96% rename from lib/src/model/graph.rs rename to lib/oxrdf/src/graph.rs index b02a47ba..deb9a623 100644 --- a/lib/src/model/graph.rs +++ b/lib/oxrdf/src/graph.rs @@ -2,7 +2,7 @@ //! //! Usage example: //! ``` -//! use oxigraph::model::*; +//! use oxrdf::*; //! //! let mut graph = Graph::default(); //! @@ -19,8 +19,8 @@ //! //! See also [`Dataset`](super::Dataset) if you want to get support of multiple RDF graphs at the same time. -use crate::model::dataset::*; -use crate::model::*; +use crate::dataset::*; +use crate::*; use std::fmt; /// An in-memory [RDF graph](https://www.w3.org/TR/rdf11-concepts/#dfn-graph). @@ -31,7 +31,7 @@ use std::fmt; /// /// Usage example: /// ``` -/// use oxigraph::model::*; +/// use oxrdf::*; /// /// let mut graph = Graph::default(); /// @@ -181,7 +181,7 @@ impl Graph { /// /// Usage example ([Graph isomorphim](https://www.w3.org/TR/rdf11-concepts/#dfn-graph-isomorphism)): /// ``` - /// use oxigraph::model::*; + /// use oxrdf::*; /// /// let iri = NamedNodeRef::new("http://example.com")?; /// @@ -192,8 +192,8 @@ impl Graph { /// /// let mut graph2 = Graph::new(); /// let bnode2 = BlankNode::default(); - /// graph1.insert(TripleRef::new(iri, iri, &bnode2)); - /// graph1.insert(TripleRef::new(&bnode2, iri, iri)); + /// graph2.insert(TripleRef::new(iri, iri, &bnode2)); + /// graph2.insert(TripleRef::new(&bnode2, iri, iri)); /// /// assert_ne!(graph1, graph2); /// graph1.canonicalize(); diff --git a/lib/src/model/interning.rs b/lib/oxrdf/src/interning.rs similarity index 99% rename from lib/src/model/interning.rs rename to lib/oxrdf/src/interning.rs index 5c4c7b60..434182d2 100644 --- a/lib/src/model/interning.rs +++ b/lib/oxrdf/src/interning.rs @@ -1,6 +1,6 @@ //! Interning of RDF elements using Rodeo -use crate::model::*; +use crate::*; use lasso::{Key, Rodeo, Spur}; use std::collections::HashMap; diff --git a/lib/oxrdf/src/lib.rs b/lib/oxrdf/src/lib.rs new file mode 100644 index 00000000..c33d50b1 --- /dev/null +++ b/lib/oxrdf/src/lib.rs @@ -0,0 +1,51 @@ +//! OxRDF is a simple library providing datastructures encoding [RDF 1.1 concepts](https://www.w3.org/TR/rdf11-concepts/). +//! +//! 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). +//! +//! Inspired by [RDF/JS](https://rdf.js.org/data-model-spec/) and [Apache Commons RDF](http://commons.apache.org/proper/commons-rdf/). +//! +//! Usage example: +//! +//! Usage example: +//! ``` +//! use oxrdf::*; +//! +//! let mut graph = Graph::default(); +//! +//! // insertion +//! let ex = NamedNodeRef::new("http://example.com")?; +//! let triple = TripleRef::new(ex, ex, ex); +//! graph.insert(triple); +//! +//! // simple filter +//! let results: Vec<_> = graph.triples_for_subject(ex).collect(); +//! assert_eq!(vec![triple], results); +//! # Result::<_,Box>::Ok(()) +//! ``` + +mod blank_node; +pub mod dataset; +pub mod graph; +mod interning; +mod literal; +mod named_node; +mod parser; +#[cfg(feature = "sophia_api")] +mod sophia; +mod triple; +mod variable; +pub mod vocab; + +pub use crate::blank_node::{BlankNode, BlankNodeIdParseError, BlankNodeRef}; +pub use crate::dataset::Dataset; +pub use crate::graph::Graph; +pub use crate::literal::{Literal, LiteralRef}; +pub use crate::named_node::{NamedNode, NamedNodeRef}; +pub use crate::parser::TermParseError; +pub use crate::triple::{ + GraphName, GraphNameRef, NamedOrBlankNode, NamedOrBlankNodeRef, Quad, QuadRef, Subject, + SubjectRef, Term, TermRef, Triple, TripleRef, +}; +pub use crate::variable::{Variable, VariableNameParseError}; +pub use oxilangtag::LanguageTagParseError; +pub use oxiri::IriParseError; diff --git a/lib/src/model/literal.rs b/lib/oxrdf/src/literal.rs similarity index 82% rename from lib/src/model/literal.rs rename to lib/oxrdf/src/literal.rs index 4671282d..e65fc71b 100644 --- a/lib/src/model/literal.rs +++ b/lib/oxrdf/src/literal.rs @@ -1,8 +1,7 @@ -use crate::model::named_node::NamedNode; -use crate::model::vocab::rdf; -use crate::model::vocab::xsd; -use crate::model::NamedNodeRef; -use crate::xsd::*; +use crate::named_node::NamedNode; +use crate::vocab::rdf; +use crate::vocab::xsd; +use crate::NamedNodeRef; use oxilangtag::{LanguageTag, LanguageTagParseError}; use rio_api::model as rio; use std::borrow::Cow; @@ -14,8 +13,8 @@ use std::option::Option; /// The default string formatter is returning an N-Triples, Turtle and SPARQL compatible representation: /// ``` /// # use oxilangtag::LanguageTagParseError; -/// use oxigraph::model::Literal; -/// use oxigraph::model::vocab::xsd; +/// use oxrdf::Literal; +/// use oxrdf::vocab::xsd; /// /// assert_eq!( /// "\"foo\\nbar\"", @@ -265,15 +264,14 @@ impl From for Literal { impl From for Literal { #[inline] fn from(value: f32) -> Self { - Float::from(value).into() - } -} - -impl From for Literal { - #[inline] - fn from(value: Float) -> Self { Self(LiteralContent::TypedLiteral { - value: value.to_string(), + value: if value == f32::INFINITY { + "INF".to_string() + } else if value == f32::NEG_INFINITY { + "-INF".to_string() + } else { + value.to_string() + }, datatype: xsd::FLOAT.into(), }) } @@ -282,147 +280,25 @@ impl From for Literal { impl From for Literal { #[inline] fn from(value: f64) -> Self { - Double::from(value).into() - } -} - -impl From for Literal { - #[inline] - fn from(value: Double) -> Self { Self(LiteralContent::TypedLiteral { - value: value.to_string(), + value: if value == f64::INFINITY { + "INF".to_string() + } else if value == f64::NEG_INFINITY { + "-INF".to_string() + } else { + value.to_string() + }, datatype: xsd::DOUBLE.into(), }) } } -impl From for Literal { - #[inline] - fn from(value: Decimal) -> Self { - Self(LiteralContent::TypedLiteral { - value: value.to_string(), - datatype: xsd::DECIMAL.into(), - }) - } -} - -impl From for Literal { - #[inline] - fn from(value: DateTime) -> Self { - Self(LiteralContent::TypedLiteral { - value: value.to_string(), - datatype: xsd::DATE_TIME.into(), - }) - } -} - -impl From