commit c20417c18c1209d5f2c61c998bd0c81d1e017016
Author: Niko PLP
Date: Fri May 17 02:34:33 2024 +0300
Squashed 'oxigraph/' content from commit c5e23fb0
git-subtree-dir: oxigraph
git-subtree-split: c5e23fb0dd1a6b6cc36a4d021276df46f55bc6e4
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..52caf88
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,59 @@
+[package]
+name = "oxigraph"
+version.workspace = true
+authors.workspace = true
+license.workspace = true
+readme = "README.md"
+keywords = ["RDF", "SPARQL", "graph-database", "database"]
+categories = ["database-implementations"]
+repository = "https://github.com/oxigraph/oxigraph/tree/main/lib/oxigraph"
+homepage = "https://oxigraph.org/"
+documentation = "https://docs.rs/oxigraph"
+description = """
+a SPARQL database and RDF toolkit
+"""
+edition.workspace = true
+rust-version.workspace = true
+
+[features]
+js = ["getrandom/js", "oxsdatatypes/js", "js-sys"]
+
+
+[dependencies]
+digest.workspace = true
+hex.workspace = true
+json-event-parser.workspace = true
+md-5.workspace = true
+oxilangtag.workspace = true
+oxiri.workspace = true
+oxrdf = { workspace = true, features = ["rdf-star", "oxsdatatypes"] }
+oxrdfio = { workspace = true, features = ["rdf-star"] }
+oxsdatatypes.workspace = true
+rand.workspace = true
+regex.workspace = true
+sha1.workspace = true
+sha2.workspace = true
+siphasher.workspace = true
+sparesults = { workspace = true, features = ["rdf-star"] }
+spargebra = { workspace = true, features = ["rdf-star", "sep-0002", "sep-0006"] }
+sparopt = { workspace = true, features = ["rdf-star", "sep-0002", "sep-0006"] }
+thiserror.workspace = true
+
+[target.'cfg(not(target_family = "wasm"))'.dependencies]
+libc = "0.2"
+rocksdb.workspace = true
+
+[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dependencies]
+getrandom.workspace = true
+js-sys = { workspace = true, optional = true }
+
+[target.'cfg(not(target_family = "wasm"))'.dev-dependencies]
+codspeed-criterion-compat.workspace = true
+zstd.workspace = true
+
+[lints]
+workspace = true
+
+[package.metadata.docs.rs]
+rustdoc-args = ["--cfg", "docsrs"]
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..293d7cd
--- /dev/null
+++ b/README.md
@@ -0,0 +1,82 @@
+Oxigraph
+========
+
+[![Latest Version](https://img.shields.io/crates/v/oxigraph.svg)](https://crates.io/crates/oxigraph)
+[![Released API docs](https://docs.rs/oxigraph/badge.svg)](https://docs.rs/oxigraph)
+[![Crates.io downloads](https://img.shields.io/crates/d/oxigraph)](https://crates.io/crates/oxigraph)
+[![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)
+
+Oxigraph is a graph database library implementing the [SPARQL](https://www.w3.org/TR/sparql11-overview/) standard.
+
+Its goal is to provide a compliant, safe and fast on-disk graph database.
+It also provides a set of utility functions for reading, writing, and processing RDF files.
+
+Oxigraph is in heavy development and SPARQL query evaluation has not been optimized yet.
+
+Oxigraph also provides [a CLI tool](https://crates.io/crates/oxigraph-cli) and [a Python library](https://pyoxigraph.readthedocs.io/) based on this library.
+
+
+Oxigraph implements the following specifications:
+* [SPARQL 1.1 Query](https://www.w3.org/TR/sparql11-query/), [SPARQL 1.1 Update](https://www.w3.org/TR/sparql11-update/), and [SPARQL 1.1 Federated Query](https://www.w3.org/TR/sparql11-federated-query/).
+* [Turtle](https://www.w3.org/TR/turtle/), [TriG](https://www.w3.org/TR/trig/), [N-Triples](https://www.w3.org/TR/n-triples/), [N-Quads](https://www.w3.org/TR/n-quads/), and [RDF/XML](https://www.w3.org/TR/rdf-syntax-grammar/) RDF serialization formats for both data ingestion and retrieval.
+* [SPARQL Query Results XML Format](https://www.w3.org/TR/rdf-sparql-XMLres/), [SPARQL 1.1 Query Results JSON Format](https://www.w3.org/TR/sparql11-results-json/) and [SPARQL 1.1 Query Results CSV and TSV Formats](https://www.w3.org/TR/sparql11-results-csv-tsv/).
+
+A preliminary benchmark [is provided](../bench/README.md). Oxigraph internal design [is described on the wiki](https://github.com/oxigraph/oxigraph/wiki/Architecture).
+
+The main entry point of Oxigraph is the [`Store`](store::Store) struct:
+```rust
+use oxigraph::store::Store;
+use oxigraph::model::*;
+use oxigraph::sparql::QueryResults;
+
+let store = Store::new().unwrap();
+
+// insertion
+let ex = NamedNode::new("http://example.com").unwrap();
+let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), GraphName::DefaultGraph);
+store.insert(&quad).unwrap();
+
+// quad filter
+let results = store.quads_for_pattern(Some(ex.as_ref().into()), None, None, None).collect::,_>>().unwrap();
+assert_eq!(vec![quad], results);
+
+// SPARQL query
+if let QueryResults::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }").unwrap() {
+ assert_eq!(solutions.next().unwrap().unwrap().get("s"), Some(&ex.into()));
+}
+```
+
+It is based on these crates that can be used separately:
+* [`oxrdf`](https://crates.io/crates/oxrdf), datastructures encoding RDF basic concepts (the [`oxigraph::model`](crate::model) module).
+* [`oxrdfio`](https://crates.io/crates/oxrdfio), a unified parser and serializer API for RDF formats (the [`oxigraph::io`](crate::io) module). It itself relies on:
+ * [`oxttl`](https://crates.io/crates/oxttl), N-Triple, N-Quad, Turtle, TriG and N3 parsing and serialization.
+ * [`oxrdfxml`](https://crates.io/crates/oxrdfxml), RDF/XML parsing and serialization.
+* [`spargebra`](https://crates.io/crates/spargebra), a SPARQL parser.
+* [`sparesults`](https://crates.io/crates/sparesults), parsers and serializers for SPARQL result formats (the [`oxigraph::sparql::results`](crate::sparql::results) module).
+* [`sparopt`](https://crates.io/crates/sparesults), a SPARQL optimizer.
+* [`oxsdatatypes`](https://crates.io/crates/oxsdatatypes), an implementation of some XML Schema datatypes.
+
+To build the library locally, don't forget to clone the submodules using `git clone --recursive https://github.com/oxigraph/oxigraph.git` to clone the repository including submodules or `git submodule update --init` to add submodules to the already cloned repository.
+
+It is possible to disable the RocksDB storage backend to only use the in-memory fallback by disabling the `rocksdb` default feature:
+```toml
+oxigraph = { version = "*", default-features = false }
+```
+This is the default behavior when compiling Oxigraph to WASM.
+
+## License
+
+This project is licensed under either of
+
+* Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or
+ ``)
+* MIT license ([LICENSE-MIT](../LICENSE-MIT) or
+ ``)
+
+at your option.
+
+
+### Contribution
+
+Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in Oxigraph 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/src/io/format.rs b/src/io/format.rs
new file mode 100644
index 0000000..08b61d8
--- /dev/null
+++ b/src/io/format.rs
@@ -0,0 +1,301 @@
+#![allow(deprecated)]
+
+use oxrdfio::{RdfFormat, RdfParser, RdfSerializer};
+
+/// [RDF graph](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-graph) serialization formats.
+///
+/// This enumeration is non exhaustive. New formats like JSON-LD will be added in the future.
+#[derive(Eq, PartialEq, Debug, Clone, Copy, Hash)]
+#[non_exhaustive]
+#[deprecated(note = "use RdfFormat instead", since = "0.4.0")]
+pub enum GraphFormat {
+ /// [N-Triples](https://www.w3.org/TR/n-triples/)
+ NTriples,
+ /// [Turtle](https://www.w3.org/TR/turtle/)
+ Turtle,
+ /// [RDF/XML](https://www.w3.org/TR/rdf-syntax-grammar/)
+ RdfXml,
+}
+
+impl GraphFormat {
+ /// The format canonical IRI according to the [Unique URIs for file formats registry](https://www.w3.org/ns/formats/).
+ ///
+ /// ```
+ /// use oxigraph::io::GraphFormat;
+ ///
+ /// assert_eq!(
+ /// GraphFormat::NTriples.iri(),
+ /// "http://www.w3.org/ns/formats/N-Triples"
+ /// )
+ /// ```
+ #[inline]
+ pub fn iri(self) -> &'static str {
+ match self {
+ Self::NTriples => "http://www.w3.org/ns/formats/N-Triples",
+ Self::Turtle => "http://www.w3.org/ns/formats/Turtle",
+ Self::RdfXml => "http://www.w3.org/ns/formats/RDF_XML",
+ }
+ }
+
+ /// The format [IANA media type](https://tools.ietf.org/html/rfc2046).
+ ///
+ /// ```
+ /// use oxigraph::io::GraphFormat;
+ ///
+ /// assert_eq!(GraphFormat::NTriples.media_type(), "application/n-triples")
+ /// ```
+ #[inline]
+ pub fn media_type(self) -> &'static str {
+ match self {
+ Self::NTriples => "application/n-triples",
+ Self::Turtle => "text/turtle",
+ Self::RdfXml => "application/rdf+xml",
+ }
+ }
+
+ /// The format [IANA-registered](https://tools.ietf.org/html/rfc2046) file extension.
+ ///
+ /// ```
+ /// use oxigraph::io::GraphFormat;
+ ///
+ /// assert_eq!(GraphFormat::NTriples.file_extension(), "nt")
+ /// ```
+ #[inline]
+ pub fn file_extension(self) -> &'static str {
+ match self {
+ Self::NTriples => "nt",
+ Self::Turtle => "ttl",
+ Self::RdfXml => "rdf",
+ }
+ }
+
+ /// Looks for a known format from a media type.
+ ///
+ /// It supports some media type aliases.
+ /// For example, "application/xml" is going to return `GraphFormat::RdfXml` even if it is not its canonical media type.
+ ///
+ /// Example:
+ /// ```
+ /// use oxigraph::io::GraphFormat;
+ ///
+ /// assert_eq!(
+ /// GraphFormat::from_media_type("text/turtle; charset=utf-8"),
+ /// Some(GraphFormat::Turtle)
+ /// )
+ /// ```
+ #[inline]
+ pub fn from_media_type(media_type: &str) -> Option {
+ match media_type.split(';').next()?.trim() {
+ "application/n-triples" | "text/plain" => Some(Self::NTriples),
+ "text/turtle" | "application/turtle" | "application/x-turtle" => Some(Self::Turtle),
+ "application/rdf+xml" | "application/xml" | "text/xml" => Some(Self::RdfXml),
+ _ => None,
+ }
+ }
+
+ /// Looks for a known format from an extension.
+ ///
+ /// It supports some aliases.
+ ///
+ /// Example:
+ /// ```
+ /// use oxigraph::io::GraphFormat;
+ ///
+ /// assert_eq!(
+ /// GraphFormat::from_extension("nt"),
+ /// Some(GraphFormat::NTriples)
+ /// )
+ /// ```
+ #[inline]
+ pub fn from_extension(extension: &str) -> Option {
+ match extension {
+ "nt" | "txt" => Some(Self::NTriples),
+ "ttl" => Some(Self::Turtle),
+ "rdf" | "xml" => Some(Self::RdfXml),
+ _ => None,
+ }
+ }
+}
+
+impl From for RdfFormat {
+ #[inline]
+ fn from(format: GraphFormat) -> Self {
+ match format {
+ GraphFormat::NTriples => Self::NTriples,
+ GraphFormat::Turtle => Self::Turtle,
+ GraphFormat::RdfXml => Self::RdfXml,
+ }
+ }
+}
+
+impl From for RdfParser {
+ #[inline]
+ fn from(format: GraphFormat) -> Self {
+ RdfFormat::from(format).into()
+ }
+}
+
+impl From for RdfSerializer {
+ #[inline]
+ fn from(format: GraphFormat) -> Self {
+ RdfFormat::from(format).into()
+ }
+}
+
+/// [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset) serialization formats.
+///
+/// This enumeration is non exhaustive. New formats like JSON-LD will be added in the future.
+#[derive(Eq, PartialEq, Debug, Clone, Copy, Hash)]
+#[non_exhaustive]
+#[deprecated(note = "use RdfFormat instead", since = "0.4.0")]
+pub enum DatasetFormat {
+ /// [N-Quads](https://www.w3.org/TR/n-quads/)
+ NQuads,
+ /// [TriG](https://www.w3.org/TR/trig/)
+ TriG,
+}
+
+impl DatasetFormat {
+ /// The format canonical IRI according to the [Unique URIs for file formats registry](https://www.w3.org/ns/formats/).
+ ///
+ /// ```
+ /// use oxigraph::io::DatasetFormat;
+ ///
+ /// assert_eq!(
+ /// DatasetFormat::NQuads.iri(),
+ /// "http://www.w3.org/ns/formats/N-Quads"
+ /// )
+ /// ```
+ #[inline]
+ pub fn iri(self) -> &'static str {
+ match self {
+ Self::NQuads => "http://www.w3.org/ns/formats/N-Quads",
+ Self::TriG => "http://www.w3.org/ns/formats/TriG",
+ }
+ }
+
+ /// The format [IANA media type](https://tools.ietf.org/html/rfc2046).
+ ///
+ /// ```
+ /// use oxigraph::io::DatasetFormat;
+ ///
+ /// assert_eq!(DatasetFormat::NQuads.media_type(), "application/n-quads")
+ /// ```
+ #[inline]
+ pub fn media_type(self) -> &'static str {
+ match self {
+ Self::NQuads => "application/n-quads",
+ Self::TriG => "application/trig",
+ }
+ }
+
+ /// The format [IANA-registered](https://tools.ietf.org/html/rfc2046) file extension.
+ ///
+ /// ```
+ /// use oxigraph::io::DatasetFormat;
+ ///
+ /// assert_eq!(DatasetFormat::NQuads.file_extension(), "nq")
+ /// ```
+ #[inline]
+ pub fn file_extension(self) -> &'static str {
+ match self {
+ Self::NQuads => "nq",
+ Self::TriG => "trig",
+ }
+ }
+
+ /// Looks for a known format from a media type.
+ ///
+ /// It supports some media type aliases.
+ ///
+ /// Example:
+ /// ```
+ /// use oxigraph::io::DatasetFormat;
+ ///
+ /// assert_eq!(
+ /// DatasetFormat::from_media_type("application/n-quads; charset=utf-8"),
+ /// Some(DatasetFormat::NQuads)
+ /// )
+ /// ```
+ #[inline]
+ pub fn from_media_type(media_type: &str) -> Option {
+ match media_type.split(';').next()?.trim() {
+ "application/n-quads" | "text/x-nquads" | "text/nquads" => Some(Self::NQuads),
+ "application/trig" | "application/x-trig" => Some(Self::TriG),
+ _ => None,
+ }
+ }
+
+ /// Looks for a known format from an extension.
+ ///
+ /// It supports some aliases.
+ ///
+ /// Example:
+ /// ```
+ /// use oxigraph::io::DatasetFormat;
+ ///
+ /// assert_eq!(
+ /// DatasetFormat::from_extension("nq"),
+ /// Some(DatasetFormat::NQuads)
+ /// )
+ /// ```
+ #[inline]
+ pub fn from_extension(extension: &str) -> Option {
+ match extension {
+ "nq" | "txt" => Some(Self::NQuads),
+ "trig" => Some(Self::TriG),
+ _ => None,
+ }
+ }
+}
+
+impl From for RdfFormat {
+ #[inline]
+ fn from(format: DatasetFormat) -> Self {
+ match format {
+ DatasetFormat::NQuads => Self::NQuads,
+ DatasetFormat::TriG => Self::TriG,
+ }
+ }
+}
+
+impl From for RdfParser {
+ #[inline]
+ fn from(format: DatasetFormat) -> Self {
+ RdfFormat::from(format).into()
+ }
+}
+
+impl From for RdfSerializer {
+ #[inline]
+ fn from(format: DatasetFormat) -> Self {
+ RdfFormat::from(format).into()
+ }
+}
+
+impl TryFrom for GraphFormat {
+ type Error = ();
+
+ /// Attempts to find a graph format that is a subset of this [`DatasetFormat`].
+ #[inline]
+ fn try_from(value: DatasetFormat) -> Result {
+ match value {
+ DatasetFormat::NQuads => Ok(Self::NTriples),
+ DatasetFormat::TriG => Ok(Self::Turtle),
+ }
+ }
+}
+
+impl TryFrom for DatasetFormat {
+ type Error = ();
+
+ /// Attempts to find a dataset format that is a superset of this [`GraphFormat`].
+ #[inline]
+ fn try_from(value: GraphFormat) -> Result {
+ match value {
+ GraphFormat::NTriples => Ok(Self::NQuads),
+ GraphFormat::Turtle => Ok(Self::TriG),
+ GraphFormat::RdfXml => Err(()),
+ }
+ }
+}
diff --git a/src/io/mod.rs b/src/io/mod.rs
new file mode 100644
index 0000000..1b15bc8
--- /dev/null
+++ b/src/io/mod.rs
@@ -0,0 +1,39 @@
+//! Utilities to read and write RDF graphs and datasets using [OxRDF I/O](https://crates.io/crates/oxrdfio).
+//!
+//! The entry points of this module are the two [`RdfParser`] and [`RdfSerializer`] structs.
+//!
+//! Usage example converting a Turtle file to a N-Triples file:
+//! ```
+//! use oxigraph::io::{RdfFormat, RdfParser, RdfSerializer};
+//!
+//! let turtle_file = b"@base .
+//! @prefix schema: .
+//! a schema:Person ;
+//! schema:name \"Foo\" .
+//! a schema:Person ;
+//! schema:name \"Bar\" .";
+//!
+//! let ntriples_file = b" .
+//! \"Foo\" .
+//! .
+//! \"Bar\" .
+//! ";
+//!
+//! let mut writer = RdfSerializer::from_format(RdfFormat::NTriples).serialize_to_write(Vec::new());
+//! for quad in RdfParser::from_format(RdfFormat::Turtle).parse_read(turtle_file.as_ref()) {
+//! writer.write_quad(&quad.unwrap()).unwrap();
+//! }
+//! assert_eq!(writer.finish().unwrap(), ntriples_file);
+//! ```
+
+mod format;
+pub mod read;
+pub mod write;
+
+#[allow(deprecated)]
+pub use self::format::{DatasetFormat, GraphFormat};
+#[allow(deprecated)]
+pub use self::read::{DatasetParser, GraphParser};
+#[allow(deprecated)]
+pub use self::write::{DatasetSerializer, GraphSerializer};
+pub use oxrdfio::*;
diff --git a/src/io/read.rs b/src/io/read.rs
new file mode 100644
index 0000000..6d01f6f
--- /dev/null
+++ b/src/io/read.rs
@@ -0,0 +1,199 @@
+#![allow(deprecated)]
+
+//! Utilities to read RDF graphs and datasets.
+
+use crate::io::{DatasetFormat, GraphFormat};
+use crate::model::*;
+use oxrdfio::{FromReadQuadReader, RdfParseError, RdfParser};
+use std::io::Read;
+
+/// Parsers for RDF graph serialization formats.
+///
+/// It currently supports the following formats:
+/// * [N-Triples](https://www.w3.org/TR/n-triples/) ([`GraphFormat::NTriples`])
+/// * [Turtle](https://www.w3.org/TR/turtle/) ([`GraphFormat::Turtle`])
+/// * [RDF/XML](https://www.w3.org/TR/rdf-syntax-grammar/) ([`GraphFormat::RdfXml`])
+///
+/// ```
+/// use oxigraph::io::{GraphFormat, GraphParser};
+///
+/// let file = " .";
+///
+/// let parser = GraphParser::from_format(GraphFormat::NTriples);
+/// let triples = parser
+/// .read_triples(file.as_bytes())
+/// .collect::, _>>()?;
+///
+/// assert_eq!(triples.len(), 1);
+/// assert_eq!(triples[0].subject.to_string(), "");
+/// # std::io::Result::Ok(())
+/// ```
+#[deprecated(note = "use RdfParser instead", since = "0.4.0")]
+pub struct GraphParser {
+ inner: RdfParser,
+}
+
+impl GraphParser {
+ /// Builds a parser for the given format.
+ #[inline]
+ pub fn from_format(format: GraphFormat) -> Self {
+ Self {
+ inner: RdfParser::from_format(format.into())
+ .without_named_graphs()
+ .rename_blank_nodes(),
+ }
+ }
+
+ /// Provides an IRI that could be used to resolve the file relative IRIs.
+ ///
+ /// ```
+ /// use oxigraph::io::{GraphFormat, GraphParser};
+ ///
+ /// let file = "
.";
+ ///
+ /// let parser =
+ /// GraphParser::from_format(GraphFormat::Turtle).with_base_iri("http://example.com")?;
+ /// let triples = parser
+ /// .read_triples(file.as_bytes())
+ /// .collect::, _>>()?;
+ ///
+ /// assert_eq!(triples.len(), 1);
+ /// assert_eq!(triples[0].subject.to_string(), "");
+ /// # Result::<_,Box>::Ok(())
+ /// ```
+ #[inline]
+ pub fn with_base_iri(self, base_iri: impl Into) -> Result {
+ Ok(Self {
+ inner: self.inner.with_base_iri(base_iri)?,
+ })
+ }
+
+ /// Executes the parsing itself on a [`Read`] implementation and returns an iterator of triples.
+ pub fn read_triples(self, reader: R) -> TripleReader {
+ TripleReader {
+ parser: self.inner.parse_read(reader),
+ }
+ }
+}
+
+/// An iterator yielding read triples.
+/// Could be built using a [`GraphParser`].
+///
+/// ```
+/// use oxigraph::io::{GraphFormat, GraphParser};
+///
+/// let file = " .";
+///
+/// let parser = GraphParser::from_format(GraphFormat::NTriples);
+/// let triples = parser
+/// .read_triples(file.as_bytes())
+/// .collect::, _>>()?;
+///
+/// assert_eq!(triples.len(), 1);
+/// assert_eq!(triples[0].subject.to_string(), "");
+/// # std::io::Result::Ok(())
+/// ```
+#[must_use]
+pub struct TripleReader {
+ parser: FromReadQuadReader,
+}
+
+impl Iterator for TripleReader {
+ type Item = Result;
+
+ fn next(&mut self) -> Option {
+ Some(self.parser.next()?.map(Into::into).map_err(Into::into))
+ }
+}
+
+/// A parser for RDF dataset serialization formats.
+///
+/// It currently supports the following formats:
+/// * [N-Quads](https://www.w3.org/TR/n-quads/) ([`DatasetFormat::NQuads`])
+/// * [TriG](https://www.w3.org/TR/trig/) ([`DatasetFormat::TriG`])
+///
+/// ```
+/// use oxigraph::io::{DatasetFormat, DatasetParser};
+///
+/// let file = " .";
+///
+/// let parser = DatasetParser::from_format(DatasetFormat::NQuads);
+/// let quads = parser.read_quads(file.as_bytes()).collect::,_>>()?;
+///
+/// assert_eq!(quads.len(), 1);
+/// assert_eq!(quads[0].subject.to_string(), "");
+/// # std::io::Result::Ok(())
+/// ```
+#[deprecated(note = "use RdfParser instead", since = "0.4.0")]
+pub struct DatasetParser {
+ inner: RdfParser,
+}
+
+impl DatasetParser {
+ /// Builds a parser for the given format.
+ #[inline]
+ pub fn from_format(format: DatasetFormat) -> Self {
+ Self {
+ inner: RdfParser::from_format(format.into()).rename_blank_nodes(),
+ }
+ }
+
+ /// Provides an IRI that could be used to resolve the file relative IRIs.
+ ///
+ /// ```
+ /// use oxigraph::io::{DatasetFormat, DatasetParser};
+ ///
+ /// let file = " { }";
+ ///
+ /// let parser =
+ /// DatasetParser::from_format(DatasetFormat::TriG).with_base_iri("http://example.com")?;
+ /// let triples = parser
+ /// .read_quads(file.as_bytes())
+ /// .collect::, _>>()?;
+ ///
+ /// assert_eq!(triples.len(), 1);
+ /// assert_eq!(triples[0].subject.to_string(), "");
+ /// # Result::<_,Box>::Ok(())
+ /// ```
+ #[inline]
+ pub fn with_base_iri(self, base_iri: impl Into) -> Result {
+ Ok(Self {
+ inner: self.inner.with_base_iri(base_iri)?,
+ })
+ }
+
+ /// Executes the parsing itself on a [`Read`] implementation and returns an iterator of quads.
+ pub fn read_quads(self, reader: R) -> QuadReader {
+ QuadReader {
+ parser: self.inner.parse_read(reader),
+ }
+ }
+}
+
+/// An iterator yielding read quads.
+/// Could be built using a [`DatasetParser`].
+///
+/// ```
+/// use oxigraph::io::{DatasetFormat, DatasetParser};
+///
+/// let file = " .";
+///
+/// let parser = DatasetParser::from_format(DatasetFormat::NQuads);
+/// let quads = parser.read_quads(file.as_bytes()).collect::,_>>()?;
+///
+/// assert_eq!(quads.len(), 1);
+/// assert_eq!(quads[0].subject.to_string(), "");
+/// # std::io::Result::Ok(())
+/// ```
+#[must_use]
+pub struct QuadReader {
+ parser: FromReadQuadReader,
+}
+
+impl Iterator for QuadReader {
+ type Item = Result;
+
+ fn next(&mut self) -> Option {
+ Some(self.parser.next()?.map_err(Into::into))
+ }
+}
diff --git a/src/io/write.rs b/src/io/write.rs
new file mode 100644
index 0000000..7f27cd9
--- /dev/null
+++ b/src/io/write.rs
@@ -0,0 +1,185 @@
+#![allow(deprecated)]
+
+//! Utilities to write RDF graphs and datasets.
+
+use crate::io::{DatasetFormat, GraphFormat};
+use crate::model::*;
+use oxrdfio::{RdfSerializer, ToWriteQuadWriter};
+use std::io::{self, Write};
+
+/// A serializer for RDF graph serialization formats.
+///
+/// It currently supports the following formats:
+/// * [N-Triples](https://www.w3.org/TR/n-triples/) ([`GraphFormat::NTriples`])
+/// * [Turtle](https://www.w3.org/TR/turtle/) ([`GraphFormat::Turtle`])
+/// * [RDF/XML](https://www.w3.org/TR/rdf-syntax-grammar/) ([`GraphFormat::RdfXml`])
+///
+/// ```
+/// use oxigraph::io::{GraphFormat, GraphSerializer};
+/// use oxigraph::model::*;
+///
+/// let mut buffer = Vec::new();
+/// let mut writer = GraphSerializer::from_format(GraphFormat::NTriples).triple_writer(&mut buffer);
+/// writer.write(&Triple {
+/// subject: NamedNode::new("http://example.com/s")?.into(),
+/// predicate: NamedNode::new("http://example.com/p")?,
+/// object: NamedNode::new("http://example.com/o")?.into(),
+/// })?;
+/// writer.finish()?;
+///
+/// assert_eq!(
+/// buffer.as_slice(),
+/// " .\n".as_bytes()
+/// );
+/// # Result::<_,Box>::Ok(())
+/// ```
+#[deprecated(note = "use RdfSerializer instead", since = "0.4.0")]
+pub struct GraphSerializer {
+ inner: RdfSerializer,
+}
+
+impl GraphSerializer {
+ /// Builds a serializer for the given format
+ #[inline]
+ pub fn from_format(format: GraphFormat) -> Self {
+ Self {
+ inner: RdfSerializer::from_format(format.into()),
+ }
+ }
+
+ /// Returns a [`TripleWriter`] allowing writing triples into the given [`Write`] implementation
+ pub fn triple_writer(self, write: W) -> TripleWriter {
+ TripleWriter {
+ writer: self.inner.serialize_to_write(write),
+ }
+ }
+}
+
+/// Allows writing triples.
+/// Could be built using a [`GraphSerializer`].
+///
+///
+///
+/// Do not forget to run the [`finish`](TripleWriter::finish()) method to properly write the last bytes of the file.
+///
+/// ```
+/// use oxigraph::io::{GraphFormat, GraphSerializer};
+/// use oxigraph::model::*;
+///
+/// let mut buffer = Vec::new();
+/// let mut writer = GraphSerializer::from_format(GraphFormat::NTriples).triple_writer(&mut buffer);
+/// writer.write(&Triple {
+/// subject: NamedNode::new("http://example.com/s")?.into(),
+/// predicate: NamedNode::new("http://example.com/p")?,
+/// object: NamedNode::new("http://example.com/o")?.into(),
+/// })?;
+/// writer.finish()?;
+///
+/// assert_eq!(
+/// buffer.as_slice(),
+/// " .\n".as_bytes()
+/// );
+/// # Result::<_,Box>::Ok(())
+/// ```
+#[must_use]
+pub struct TripleWriter {
+ writer: ToWriteQuadWriter,
+}
+
+impl TripleWriter {
+ /// Writes a triple
+ pub fn write<'a>(&mut self, triple: impl Into>) -> io::Result<()> {
+ self.writer.write_triple(triple)
+ }
+
+ /// Writes the last bytes of the file
+ pub fn finish(self) -> io::Result<()> {
+ self.writer.finish()?.flush()
+ }
+}
+
+/// A serializer for RDF graph serialization formats.
+///
+/// It currently supports the following formats:
+/// * [N-Quads](https://www.w3.org/TR/n-quads/) ([`DatasetFormat::NQuads`])
+/// * [TriG](https://www.w3.org/TR/trig/) ([`DatasetFormat::TriG`])
+///
+/// ```
+/// use oxigraph::io::{DatasetFormat, DatasetSerializer};
+/// use oxigraph::model::*;
+///
+/// let mut buffer = Vec::new();
+/// let mut writer = DatasetSerializer::from_format(DatasetFormat::NQuads).quad_writer(&mut buffer);
+/// writer.write(&Quad {
+/// subject: NamedNode::new("http://example.com/s")?.into(),
+/// predicate: NamedNode::new("http://example.com/p")?,
+/// object: NamedNode::new("http://example.com/o")?.into(),
+/// graph_name: NamedNode::new("http://example.com/g")?.into(),
+/// })?;
+/// writer.finish()?;
+///
+/// assert_eq!(buffer.as_slice(), " .\n".as_bytes());
+/// # Result::<_,Box>::Ok(())
+/// ```
+#[deprecated(note = "use RdfSerializer instead", since = "0.4.0")]
+pub struct DatasetSerializer {
+ inner: RdfSerializer,
+}
+
+impl DatasetSerializer {
+ /// Builds a serializer for the given format
+ #[inline]
+ pub fn from_format(format: DatasetFormat) -> Self {
+ Self {
+ inner: RdfSerializer::from_format(format.into()),
+ }
+ }
+
+ /// Returns a [`QuadWriter`] allowing writing triples into the given [`Write`] implementation
+ pub fn quad_writer(self, write: W) -> QuadWriter {
+ QuadWriter {
+ writer: self.inner.serialize_to_write(write),
+ }
+ }
+}
+
+/// Allows writing triples.
+/// Could be built using a [`DatasetSerializer`].
+///
+///
+///
+/// Do not forget to run the [`finish`](QuadWriter::finish()) method to properly write the last bytes of the file.
+///
+/// ```
+/// use oxigraph::io::{DatasetFormat, DatasetSerializer};
+/// use oxigraph::model::*;
+///
+/// let mut buffer = Vec::new();
+/// let mut writer = DatasetSerializer::from_format(DatasetFormat::NQuads).quad_writer(&mut buffer);
+/// writer.write(&Quad {
+/// subject: NamedNode::new("http://example.com/s")?.into(),
+/// predicate: NamedNode::new("http://example.com/p")?,
+/// object: NamedNode::new("http://example.com/o")?.into(),
+/// graph_name: NamedNode::new("http://example.com/g")?.into(),
+/// })?;
+/// writer.finish()?;
+///
+/// assert_eq!(buffer.as_slice(), " .\n".as_bytes());
+/// # Result::<_,Box>::Ok(())
+/// ```
+#[must_use]
+pub struct QuadWriter {
+ writer: ToWriteQuadWriter,
+}
+
+impl QuadWriter {
+ /// Writes a quad
+ pub fn write<'a>(&mut self, quad: impl Into>) -> io::Result<()> {
+ self.writer.write_quad(quad)
+ }
+
+ /// Writes the last bytes of the file
+ pub fn finish(self) -> io::Result<()> {
+ self.writer.finish()?.flush()
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..b36c4d6
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,12 @@
+#![doc = include_str!("../README.md")]
+#![doc(test(attr(deny(warnings))))]
+#![doc(test(attr(allow(deprecated))))]
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
+#![doc(html_favicon_url = "https://raw.githubusercontent.com/oxigraph/oxigraph/main/logo.svg")]
+#![doc(html_logo_url = "https://raw.githubusercontent.com/oxigraph/oxigraph/main/logo.svg")]
+
+pub mod io;
+pub mod model;
+pub mod sparql;
+mod storage;
+pub mod store;
diff --git a/src/model.rs b/src/model.rs
new file mode 100644
index 0000000..dbca934
--- /dev/null
+++ b/src/model.rs
@@ -0,0 +1,22 @@
+//! Implements data structures for [RDF 1.1 Concepts](https://www.w3.org/TR/rdf11-concepts/) using [OxRDF](https://crates.io/crates/oxrdf).
+//!
+//! Usage example:
+//!
+//! ```
+//! use oxigraph::model::*;
+//!
+//! let mut graph = Graph::default();
+//!
+//! // insertion
+//! let ex = NamedNodeRef::new("http://example.com").unwrap();
+//! 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);
+//! ```
+
+pub use oxrdf::*;
+
+pub use spargebra::term::GroundQuad;
diff --git a/src/sparql/algebra.rs b/src/sparql/algebra.rs
new file mode 100644
index 0000000..8b3f385
--- /dev/null
+++ b/src/sparql/algebra.rs
@@ -0,0 +1,311 @@
+//! [SPARQL 1.1 Query Algebra](https://www.w3.org/TR/sparql11-query/#sparqlQuery)
+//!
+//! The root type for SPARQL queries is [`Query`] and the root type for updates is [`Update`].
+
+use crate::model::*;
+use crate::sparql::eval::Timer;
+use oxsdatatypes::DayTimeDuration;
+use spargebra::GraphUpdateOperation;
+use std::fmt;
+use std::str::FromStr;
+
+/// A parsed [SPARQL query](https://www.w3.org/TR/sparql11-query/).
+///
+/// ```
+/// use oxigraph::model::NamedNode;
+/// use oxigraph::sparql::Query;
+///
+/// let query_str = "SELECT ?s ?p ?o WHERE { ?s ?p ?o . }";
+/// let mut query = Query::parse(query_str, None)?;
+///
+/// assert_eq!(query.to_string(), query_str);
+///
+/// // We edit the query dataset specification
+/// let default = vec![NamedNode::new("http://example.com")?.into()];
+/// query.dataset_mut().set_default_graph(default.clone());
+/// assert_eq!(
+/// query.dataset().default_graph_graphs(),
+/// Some(default.as_slice())
+/// );
+/// # Ok::<_, Box>(())
+/// ```
+#[derive(Eq, PartialEq, Debug, Clone, Hash)]
+pub struct Query {
+ pub(super) inner: spargebra::Query,
+ pub(super) dataset: QueryDataset,
+ pub(super) parsing_duration: Option,
+}
+
+impl Query {
+ /// Parses a SPARQL query with an optional base IRI to resolve relative IRIs in the query.
+ pub fn parse(
+ query: &str,
+ base_iri: Option<&str>,
+ ) -> Result {
+ let start = Timer::now();
+ let query = Self::from(spargebra::Query::parse(query, base_iri)?);
+ Ok(Self {
+ dataset: query.dataset,
+ inner: query.inner,
+ parsing_duration: start.elapsed(),
+ })
+ }
+
+ /// Returns [the query dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset)
+ pub fn dataset(&self) -> &QueryDataset {
+ &self.dataset
+ }
+
+ /// Returns [the query dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset)
+ pub fn dataset_mut(&mut self) -> &mut QueryDataset {
+ &mut self.dataset
+ }
+}
+
+impl fmt::Display for Query {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.inner.fmt(f) // TODO: override
+ }
+}
+
+impl FromStr for Query {
+ type Err = spargebra::SparqlSyntaxError;
+
+ fn from_str(query: &str) -> Result {
+ Self::parse(query, None)
+ }
+}
+
+impl TryFrom<&str> for Query {
+ type Error = spargebra::SparqlSyntaxError;
+
+ fn try_from(query: &str) -> Result {
+ Self::from_str(query)
+ }
+}
+
+impl TryFrom<&String> for Query {
+ type Error = spargebra::SparqlSyntaxError;
+
+ fn try_from(query: &String) -> Result {
+ Self::from_str(query)
+ }
+}
+
+impl From for Query {
+ fn from(query: spargebra::Query) -> Self {
+ Self {
+ dataset: QueryDataset::from_algebra(match &query {
+ spargebra::Query::Select { dataset, .. }
+ | spargebra::Query::Construct { dataset, .. }
+ | spargebra::Query::Describe { dataset, .. }
+ | spargebra::Query::Ask { dataset, .. } => dataset,
+ }),
+ inner: query,
+ parsing_duration: None,
+ }
+ }
+}
+
+/// A parsed [SPARQL update](https://www.w3.org/TR/sparql11-update/).
+///
+/// ```
+/// use oxigraph::sparql::Update;
+///
+/// let update_str = "CLEAR ALL ;";
+/// let update = Update::parse(update_str, None)?;
+///
+/// assert_eq!(update.to_string().trim(), update_str);
+/// # Ok::<_, oxigraph::sparql::SparqlSyntaxError>(())
+/// ```
+#[derive(Eq, PartialEq, Debug, Clone, Hash)]
+pub struct Update {
+ pub(super) inner: spargebra::Update,
+ pub(super) using_datasets: Vec