Adopt new I/O API for serialization

pull/592/head
Tpt 1 year ago committed by Thomas Tanon
parent 7cd383af79
commit 217abaf7ee
  1. 30
      js/src/store.rs
  2. 1
      lib/src/io/mod.rs
  3. 4
      lib/src/io/write.rs
  4. 11
      lib/src/sparql/error.rs
  5. 13
      lib/src/sparql/model.rs
  6. 21
      lib/src/sparql/update.rs
  7. 12
      lib/src/storage/error.rs
  8. 24
      lib/src/store.rs
  9. 10
      lib/tests/store.rs
  10. 41
      python/src/io.rs
  11. 4
      python/src/sparql.rs
  12. 36
      python/src/store.rs
  13. 4
      python/tests/test_store.py
  14. 154
      server/src/main.rs

@ -4,7 +4,7 @@ use crate::format_err;
use crate::model::*;
use crate::utils::to_err;
use js_sys::{Array, Map};
use oxigraph::io::{DatasetFormat, GraphFormat};
use oxigraph::io::{DatasetFormat, GraphFormat, RdfFormat};
use oxigraph::model::*;
use oxigraph::sparql::QueryResults;
use oxigraph::store::Store;
@ -191,34 +191,22 @@ impl JsStore {
}
pub fn dump(&self, mime_type: &str, from_graph_name: &JsValue) -> Result<String, JsValue> {
let Some(format) = RdfFormat::from_media_type(mime_type) else {
return Err(format_err!("Not supported MIME type: {mime_type}"));
};
let from_graph_name =
if let Some(graph_name) = FROM_JS.with(|c| c.to_optional_term(from_graph_name))? {
Some(graph_name.try_into()?)
Some(GraphName::try_from(graph_name)?)
} else {
None
};
let mut buffer = Vec::new();
if let Some(graph_format) = GraphFormat::from_media_type(mime_type) {
self.store
.dump_graph(
&mut buffer,
graph_format,
&from_graph_name.unwrap_or(GraphName::DefaultGraph),
)
.map_err(to_err)?;
} else if let Some(dataset_format) = DatasetFormat::from_media_type(mime_type) {
if from_graph_name.is_some() {
return Err(format_err!(
"The target graph name parameter is not available for dataset formats"
));
}
self.store
.dump_dataset(&mut buffer, dataset_format)
.map_err(to_err)?;
if let Some(from_graph_name) = &from_graph_name {
self.store.dump_graph(&mut buffer, format, from_graph_name)
} else {
return Err(format_err!("Not supported MIME type: {mime_type}"));
self.store.dump_dataset(&mut buffer, format)
}
.map_err(to_err)?;
String::from_utf8(buffer).map_err(to_err)
}
}

@ -7,6 +7,7 @@ pub mod write;
pub use self::format::{DatasetFormat, GraphFormat};
pub use self::read::{DatasetParser, GraphParser};
#[allow(deprecated)]
pub use self::write::{DatasetSerializer, GraphSerializer};
pub use oxrdfio::{
FromReadQuadReader, ParseError, RdfFormat, RdfParser, RdfSerializer, SyntaxError,

@ -1,3 +1,5 @@
#![allow(deprecated)]
//! Utilities to write RDF graphs and datasets.
use crate::io::{DatasetFormat, GraphFormat};
@ -28,6 +30,7 @@ use std::io::{self, Write};
/// assert_eq!(buffer.as_slice(), "<http://example.com/s> <http://example.com/p> <http://example.com/o> .\n".as_bytes());
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ```
#[deprecated(note = "Use RdfSerializer instead")]
pub struct GraphSerializer {
inner: RdfSerializer,
}
@ -110,6 +113,7 @@ impl<W: Write> TripleWriter<W> {
/// assert_eq!(buffer.as_slice(), "<http://example.com/s> <http://example.com/p> <http://example.com/o> <http://example.com/g> .\n".as_bytes());
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ```
#[deprecated(note = "Use RdfSerializer instead")]
pub struct DatasetSerializer {
inner: RdfSerializer,
}

@ -1,4 +1,4 @@
use crate::io::read::ParseError;
use crate::io::{ParseError, SyntaxError};
use crate::storage::StorageError;
use std::convert::Infallible;
use std::error;
@ -14,10 +14,10 @@ pub enum EvaluationError {
/// An error from the storage.
Storage(StorageError),
/// An error while parsing an external RDF file.
GraphParsing(ParseError),
GraphParsing(SyntaxError),
/// An error while parsing an external result file (likely from a federated query).
ResultsParsing(sparesults::ParseError),
/// An error returned during store IOs or during results write.
/// An error returned during store or results I/Os.
Io(io::Error),
/// An error returned during the query evaluation itself (not supported custom function...).
Query(QueryError),
@ -132,7 +132,10 @@ impl From<io::Error> for EvaluationError {
impl From<ParseError> for EvaluationError {
#[inline]
fn from(error: ParseError) -> Self {
Self::GraphParsing(error)
match error {
ParseError::Syntax(error) => Self::GraphParsing(error),
ParseError::Io(error) => Self::Io(error),
}
}
}

@ -1,5 +1,4 @@
use crate::io::GraphFormat;
use crate::io::GraphSerializer;
use crate::io::{RdfFormat, RdfSerializer};
use crate::model::*;
use crate::sparql::error::EvaluationError;
use oxrdf::{Variable, VariableRef};
@ -96,7 +95,7 @@ impl QueryResults {
///
/// ```
/// use oxigraph::store::Store;
/// use oxigraph::io::GraphFormat;
/// use oxigraph::io::{RdfFormat, GraphFormat};
/// use oxigraph::model::*;
///
/// let graph = "<http://example.com> <http://example.com> <http://example.com> .\n";
@ -105,19 +104,19 @@ impl QueryResults {
/// store.load_graph(graph.as_bytes(), GraphFormat::NTriples, GraphNameRef::DefaultGraph, None)?;
///
/// let mut results = Vec::new();
/// store.query("CONSTRUCT WHERE { ?s ?p ?o }")?.write_graph(&mut results, GraphFormat::NTriples)?;
/// store.query("CONSTRUCT WHERE { ?s ?p ?o }")?.write_graph(&mut results, RdfFormat::NTriples)?;
/// assert_eq!(results, graph.as_bytes());
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ```
pub fn write_graph(
self,
write: impl Write,
format: GraphFormat,
format: impl Into<RdfFormat>,
) -> Result<(), EvaluationError> {
if let Self::Graph(triples) = self {
let mut writer = GraphSerializer::from_format(format).triple_writer(write);
let mut writer = RdfSerializer::from_format(format.into()).serialize_to_write(write);
for triple in triples {
writer.write(&triple?)?;
writer.write_triple(&triple?)?;
}
writer.finish()?;
Ok(())

@ -1,5 +1,4 @@
use crate::io::read::ParseError;
use crate::io::{GraphFormat, GraphParser};
use crate::io::{RdfFormat, RdfParser};
use crate::model::{GraphName as OxGraphName, GraphNameRef, Quad as OxQuad};
use crate::sparql::algebra::QueryDataset;
use crate::sparql::dataset::DatasetView;
@ -166,7 +165,7 @@ impl<'a, 'b: 'a> SimpleUpdateEvaluator<'a, 'b> {
from.as_str(),
"application/n-triples, text/turtle, application/rdf+xml",
)?;
let format = GraphFormat::from_media_type(&content_type).ok_or_else(|| {
let format = RdfFormat::from_media_type(&content_type).ok_or_else(|| {
EvaluationError::msg(format!(
"Unsupported Content-Type returned by {from}: {content_type}"
))
@ -175,15 +174,17 @@ impl<'a, 'b: 'a> SimpleUpdateEvaluator<'a, 'b> {
GraphName::NamedNode(graph_name) => graph_name.into(),
GraphName::DefaultGraph => GraphNameRef::DefaultGraph,
};
let mut parser = GraphParser::from_format(format);
let mut parser = RdfParser::from_format(format)
.rename_blank_nodes()
.without_named_graphs()
.with_default_graph(to_graph_name);
if let Some(base_iri) = &self.base_iri {
parser = parser
.with_base_iri(base_iri.as_str())
.map_err(|e| ParseError::invalid_base_iri(base_iri, e))?;
parser = parser.with_base_iri(base_iri.as_str()).map_err(|e| {
EvaluationError::msg(format!("The LOAD IRI '{base_iri}' is invalid: {e}"))
})?;
}
for t in parser.read_triples(body) {
self.transaction
.insert(t?.as_ref().in_graph(to_graph_name))?;
for q in parser.parse_read(body) {
self.transaction.insert(q?.as_ref())?;
}
Ok(())
}

@ -1,4 +1,4 @@
use crate::io::read::ParseError;
use crate::io::{read::ParseError, RdfFormat};
use std::error::Error;
use std::fmt;
use std::io;
@ -179,6 +179,8 @@ pub enum SerializerError {
Io(io::Error),
/// An error raised during the lookup in the store.
Storage(StorageError),
/// A format compatible with [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset) is required.
DatasetFormatExpected(RdfFormat),
}
impl fmt::Display for SerializerError {
@ -187,6 +189,10 @@ impl fmt::Display for SerializerError {
match self {
Self::Io(e) => e.fmt(f),
Self::Storage(e) => e.fmt(f),
Self::DatasetFormatExpected(format) => write!(
f,
"A RDF format supporting datasets was expected, {format} found"
),
}
}
}
@ -197,6 +203,7 @@ impl Error for SerializerError {
match self {
Self::Io(e) => Some(e),
Self::Storage(e) => Some(e),
Self::DatasetFormatExpected(_) => None,
}
}
}
@ -221,6 +228,9 @@ impl From<SerializerError> for io::Error {
match error {
SerializerError::Storage(error) => error.into(),
SerializerError::Io(error) => error,
SerializerError::DatasetFormatExpected(_) => {
io::Error::new(io::ErrorKind::InvalidInput, error.to_string())
}
}
}
}

@ -24,9 +24,7 @@
//! # Result::<_, Box<dyn std::error::Error>>::Ok(())
//! ```
use crate::io::read::ParseError;
use crate::io::{
DatasetFormat, DatasetParser, DatasetSerializer, GraphFormat, GraphParser, GraphSerializer,
};
use crate::io::{DatasetFormat, DatasetParser, GraphFormat, GraphParser, RdfFormat, RdfSerializer};
use crate::model::*;
use crate::sparql::{
evaluate_query, evaluate_update, EvaluationError, Query, QueryExplanation, QueryOptions,
@ -612,13 +610,13 @@ impl Store {
/// ```
pub fn dump_graph<'a>(
&self,
writer: impl Write,
format: GraphFormat,
write: impl Write,
format: impl Into<RdfFormat>,
from_graph_name: impl Into<GraphNameRef<'a>>,
) -> Result<(), SerializerError> {
let mut writer = GraphSerializer::from_format(format).triple_writer(writer);
let mut writer = RdfSerializer::from_format(format.into()).serialize_to_write(write);
for quad in self.quads_for_pattern(None, None, None, Some(from_graph_name.into())) {
writer.write(quad?.as_ref())?;
writer.write_triple(quad?.as_ref())?;
}
writer.finish()?;
Ok(())
@ -642,12 +640,16 @@ impl Store {
/// ```
pub fn dump_dataset(
&self,
writer: impl Write,
format: DatasetFormat,
write: impl Write,
format: impl Into<RdfFormat>,
) -> Result<(), SerializerError> {
let mut writer = DatasetSerializer::from_format(format).quad_writer(writer);
let format = format.into();
if !format.supports_datasets() {
return Err(SerializerError::DatasetFormatExpected(format));
}
let mut writer = RdfSerializer::from_format(format).serialize_to_write(write);
for quad in self.iter() {
writer.write(&quad?)?;
writer.write_quad(&quad?)?;
}
writer.finish()?;
Ok(())

@ -1,4 +1,4 @@
use oxigraph::io::{DatasetFormat, GraphFormat};
use oxigraph::io::{DatasetFormat, GraphFormat, RdfFormat};
use oxigraph::model::vocab::{rdf, xsd};
use oxigraph::model::*;
use oxigraph::store::Store;
@ -211,11 +211,7 @@ fn test_dump_graph() -> Result<(), Box<dyn Error>> {
}
let mut buffer = Vec::new();
store.dump_graph(
&mut buffer,
GraphFormat::NTriples,
GraphNameRef::DefaultGraph,
)?;
store.dump_graph(&mut buffer, RdfFormat::NTriples, GraphNameRef::DefaultGraph)?;
assert_eq!(
buffer.into_iter().filter(|c| *c == b'\n').count(),
NUMBER_OF_TRIPLES
@ -231,7 +227,7 @@ fn test_dump_dataset() -> Result<(), Box<dyn Error>> {
}
let mut buffer = Vec::new();
store.dump_dataset(&mut buffer, DatasetFormat::NQuads)?;
store.dump_dataset(&mut buffer, RdfFormat::NQuads)?;
assert_eq!(
buffer.into_iter().filter(|c| *c == b'\n').count(),
NUMBER_OF_TRIPLES

@ -3,8 +3,9 @@
use crate::model::{PyQuad, PyTriple};
use oxigraph::io::read::{ParseError, QuadReader, TripleReader};
use oxigraph::io::{
DatasetFormat, DatasetParser, DatasetSerializer, GraphFormat, GraphParser, GraphSerializer,
DatasetFormat, DatasetParser, GraphFormat, GraphParser, RdfFormat, RdfSerializer,
};
use oxigraph::model::QuadRef;
use pyo3::exceptions::{PyIOError, PySyntaxError, PyValueError};
use pyo3::prelude::*;
use pyo3::types::PyBytes;
@ -120,34 +121,34 @@ pub fn parse(
/// b'<http://example.com> <http://example.com/p> "1" .\n'
#[pyfunction]
pub fn serialize(input: &PyAny, output: PyObject, mime_type: &str, py: Python<'_>) -> PyResult<()> {
let Some(format) = RdfFormat::from_media_type(mime_type) else {
return Err(PyValueError::new_err(format!(
"Not supported MIME type: {mime_type}"
)));
};
let output = if let Ok(path) = output.extract::<PathBuf>(py) {
PyWritable::from_file(&path, py).map_err(map_io_err)?
} else {
PyWritable::from_data(output)
};
if let Some(graph_format) = GraphFormat::from_media_type(mime_type) {
let mut writer = GraphSerializer::from_format(graph_format).triple_writer(output);
let mut writer = RdfSerializer::from_format(format).serialize_to_write(output);
for i in input.iter()? {
writer
.write(&*i?.extract::<PyRef<PyTriple>>()?)
.map_err(map_io_err)?;
let i = i?;
if let Ok(triple) = i.extract::<PyRef<PyTriple>>() {
writer.write_triple(&*triple)
} else {
let quad = i.extract::<PyRef<PyQuad>>()?;
let quad = QuadRef::from(&*quad);
if !quad.graph_name.is_default_graph() && !format.supports_datasets() {
return Err(PyValueError::new_err(
"The {format} format does not support named graphs",
));
}
writer.finish().map_err(map_io_err)?;
Ok(())
} else if let Some(dataset_format) = DatasetFormat::from_media_type(mime_type) {
let mut writer = DatasetSerializer::from_format(dataset_format).quad_writer(output);
for i in input.iter()? {
writer
.write(&*i?.extract::<PyRef<PyQuad>>()?)
.map_err(map_io_err)?;
writer.write_quad(quad)
}
writer.finish().map_err(map_io_err)?;
Ok(())
} else {
Err(PyValueError::new_err(format!(
"Not supported MIME type: {mime_type}"
)))
.map_err(map_io_err)?;
}
writer.finish().map_err(map_io_err)
}
#[pyclass(name = "TripleReader", module = "pyoxigraph")]

@ -1,4 +1,4 @@
use crate::io::{allow_threads_unsafe, map_io_err, map_parse_error};
use crate::io::{allow_threads_unsafe, map_io_err};
use crate::map_storage_error;
use crate::model::*;
use oxigraph::model::Term;
@ -234,7 +234,7 @@ pub fn map_evaluation_error(error: EvaluationError) -> PyErr {
EvaluationError::Parsing(error) => PySyntaxError::new_err(error.to_string()),
EvaluationError::Storage(error) => map_storage_error(error),
EvaluationError::Io(error) => map_io_err(error),
EvaluationError::GraphParsing(error) => map_parse_error(error),
EvaluationError::GraphParsing(error) => PySyntaxError::new_err(error.to_string()),
EvaluationError::Query(error) => PyValueError::new_err(error.to_string()),
_ => PyRuntimeError::new_err(error.to_string()),
}

@ -3,7 +3,7 @@
use crate::io::{allow_threads_unsafe, map_io_err, map_parse_error, PyReadable, PyWritable};
use crate::model::*;
use crate::sparql::*;
use oxigraph::io::{DatasetFormat, GraphFormat};
use oxigraph::io::{DatasetFormat, GraphFormat, RdfFormat};
use oxigraph::model::{GraphName, GraphNameRef};
use oxigraph::sparql::Update;
use oxigraph::store::{self, LoaderError, SerializerError, StorageError, Store};
@ -522,10 +522,10 @@ impl PyStore {
/// :type output: io(bytes) or str or pathlib.Path
/// :param mime_type: the MIME type of the RDF serialization.
/// :type mime_type: str
/// :param from_graph: if a triple based format is requested, the store graph from which dump the triples. By default, the default graph is used.
/// :param from_graph: the store graph from which dump the triples. Required if the serialization format does not support named graphs. If it does supports named graphs the full dataset is written.
/// :type from_graph: NamedNode or BlankNode or DefaultGraph or None, optional
/// :rtype: None
/// :raises ValueError: if the MIME type is not supported or the `from_graph` parameter is given with a quad syntax.
/// :raises ValueError: if the MIME type is not supported or the `from_graph` parameter is not given with a syntax not supporting named graphs.
/// :raises IOError: if an I/O error happens during a quad lookup
///
/// >>> store = Store()
@ -547,34 +547,23 @@ impl PyStore {
} else {
PyWritable::from_data(output)
};
let Some(format) = RdfFormat::from_media_type(mime_type) else {
return Err(PyValueError::new_err(format!(
"Not supported MIME type: {mime_type}"
)));
};
let from_graph_name = if let Some(graph_name) = from_graph {
Some(GraphName::from(&PyGraphNameRef::try_from(graph_name)?))
} else {
None
};
py.allow_threads(|| {
if let Some(graph_format) = GraphFormat::from_media_type(mime_type) {
self.inner
.dump_graph(
output,
graph_format,
&from_graph_name.unwrap_or(GraphName::DefaultGraph),
)
.map_err(map_serializer_error)
} else if let Some(dataset_format) = DatasetFormat::from_media_type(mime_type) {
if from_graph_name.is_some() {
return Err(PyValueError::new_err(
"The target graph name parameter is not available for dataset formats",
));
}
self.inner
.dump_dataset(output, dataset_format)
.map_err(map_serializer_error)
if let Some(from_graph_name) = &from_graph_name {
self.inner.dump_graph(output, format, from_graph_name)
} else {
Err(PyValueError::new_err(format!(
"Not supported MIME type: {mime_type}"
)))
self.inner.dump_dataset(output, format)
}
.map_err(map_serializer_error)
})
}
@ -878,6 +867,7 @@ pub fn map_serializer_error(error: SerializerError) -> PyErr {
match error {
SerializerError::Storage(error) => map_storage_error(error),
SerializerError::Io(error) => PyIOError::new_err(error.to_string()),
SerializerError::DatasetFormatExpected(_) => PyValueError::new_err(error.to_string()),
}
}

@ -321,8 +321,10 @@ class TestStore(unittest.TestCase):
)
def test_dump_with_io_error(self) -> None:
store = Store()
store.add(Quad(foo, bar, bar))
with self.assertRaises(OSError) as _, TemporaryFile("rb") as fp:
Store().dump(fp, mime_type="application/rdf+xml")
store.dump(fp, mime_type="application/trig")
def test_write_in_read(self) -> None:
store = Store()

@ -1,10 +1,10 @@
#![allow(clippy::print_stderr, clippy::cast_precision_loss, clippy::use_debug)]
use anyhow::{anyhow, bail, Context, Error};
use anyhow::{anyhow, bail, ensure, Context, Error};
use clap::{Parser, Subcommand};
use flate2::read::MultiGzDecoder;
use oxhttp::model::{Body, HeaderName, HeaderValue, Method, Request, Response, Status};
use oxhttp::Server;
use oxigraph::io::{DatasetFormat, DatasetSerializer, GraphFormat, GraphSerializer};
use oxigraph::io::{DatasetFormat, GraphFormat, RdfFormat, RdfSerializer};
use oxigraph::model::{
GraphName, GraphNameRef, IriParseError, NamedNode, NamedNodeRef, NamedOrBlankNode,
};
@ -424,9 +424,9 @@ pub fn main() -> anyhow::Result<()> {
.ok_or_else(|| anyhow!("The --location argument is required"))?,
)?;
let format = if let Some(format) = format {
GraphOrDatasetFormat::from_str(&format)?
rdf_format_from_name(&format)?
} else if let Some(file) = &file {
GraphOrDatasetFormat::from_path(file)?
rdf_format_from_path(file)?
} else {
bail!("The --format option must be set when writing to stdout")
};
@ -554,34 +554,25 @@ pub fn main() -> anyhow::Result<()> {
}
}
QueryResults::Graph(triples) => {
let format = if let Some(name) = results_format {
if let Some(format) = GraphFormat::from_extension(&name) {
format
} else if let Some(format) = GraphFormat::from_media_type(&name) {
format
} else {
bail!("The file format '{name}' is unknown")
}
let format = if let Some(name) = &results_format {
rdf_format_from_name(name)
} else if let Some(results_file) = &results_file {
format_from_path(results_file, |ext| {
GraphFormat::from_extension(ext)
.ok_or_else(|| anyhow!("The file extension '{ext}' is unknown"))
})?
rdf_format_from_path(results_file)
} else {
bail!("The --results-format option must be set when writing to stdout")
};
}?;
let serializer = RdfSerializer::from_format(format);
if let Some(results_file) = results_file {
let mut writer = GraphSerializer::from_format(format)
.triple_writer(BufWriter::new(File::create(results_file)?));
let mut writer = serializer
.serialize_to_write(BufWriter::new(File::create(results_file)?));
for triple in triples {
writer.write(triple?.as_ref())?;
writer.write_triple(triple?.as_ref())?;
}
writer.finish()?;
} else {
let mut writer =
GraphSerializer::from_format(format).triple_writer(stdout().lock());
let mut writer = serializer.serialize_to_write(stdout().lock());
for triple in triples {
writer.write(triple?.as_ref())?;
writer.write_triple(triple?.as_ref())?;
}
writer.finish()?;
}
@ -670,22 +661,15 @@ fn bulk_load(
fn dump(
store: &Store,
writer: impl Write,
format: GraphOrDatasetFormat,
format: RdfFormat,
to_graph_name: Option<GraphName>,
) -> anyhow::Result<()> {
match format {
GraphOrDatasetFormat::Graph(format) => store.dump_graph(
writer,
format,
&to_graph_name.ok_or_else(|| anyhow!("The --graph option is required when writing a graph format like NTriples, Turtle or RDF/XML"))?,
)?,
GraphOrDatasetFormat::Dataset(format) => {
if to_graph_name.is_some() {
bail!("The --graph option is not allowed when writing a dataset format like NQuads or TriG");
}
store.dump_dataset(writer, format)?
}
}
ensure!(format.supports_datasets() || to_graph_name.is_some(), "The --graph option is required when writing a format not supporting datasets like NTriples, Turtle or RDF/XML");
if let Some(to_graph_name) = &to_graph_name {
store.dump_graph(writer, format, to_graph_name)
} else {
store.dump_dataset(writer, format)
}?;
Ok(())
}
@ -761,6 +745,23 @@ impl FromStr for GraphOrDatasetFormat {
}
}
fn rdf_format_from_path(path: &Path) -> anyhow::Result<RdfFormat> {
format_from_path(path, |ext| {
RdfFormat::from_extension(ext)
.ok_or_else(|| anyhow!("The file extension '{ext}' is unknown"))
})
}
fn rdf_format_from_name(name: &str) -> anyhow::Result<RdfFormat> {
if let Some(t) = RdfFormat::from_extension(name) {
return Ok(t);
}
if let Some(t) = RdfFormat::from_media_type(name) {
return Ok(t);
}
bail!("The file format '{name}' is unknown")
}
fn serve(store: Store, bind: String, read_only: bool, cors: bool) -> anyhow::Result<()> {
let mut server = if cors {
Server::new(cors_middleware(move |request| {
@ -917,8 +918,9 @@ fn handle_request(
(path, "GET") if path.starts_with("/store") => {
if let Some(target) = store_target(request)? {
assert_that_graph_exists(&store, &target)?;
let format = graph_content_negotiation(request)?;
let triples = store.quads_for_pattern(
let format = rdf_content_negotiation(request)?;
let quads = store.quads_for_pattern(
None,
None,
None,
@ -927,14 +929,14 @@ fn handle_request(
ReadForWrite::build_response(
move |w| {
Ok((
GraphSerializer::from_format(format).triple_writer(w),
triples,
RdfSerializer::from_format(format).serialize_to_write(w),
quads,
))
},
|(mut writer, mut triples)| {
Ok(if let Some(t) = triples.next() {
writer.write(&t?.into())?;
Some((writer, triples))
|(mut writer, mut quads)| {
Ok(if let Some(q) = quads.next() {
writer.write_triple(&q?.into())?;
Some((writer, quads))
} else {
writer.finish()?;
None
@ -943,17 +945,22 @@ fn handle_request(
format.media_type(),
)
} else {
let format = dataset_content_negotiation(request)?;
let format = rdf_content_negotiation(request)?;
if !format.supports_datasets() {
return Err(bad_request(format!(
"It is not possible to serialize the full RDF dataset using {format} that does not support named graphs"
)));
}
ReadForWrite::build_response(
move |w| {
Ok((
DatasetSerializer::from_format(format).quad_writer(w),
RdfSerializer::from_format(format).serialize_to_write(w),
store.iter(),
))
},
|(mut writer, mut quads)| {
Ok(if let Some(q) = quads.next() {
writer.write(&q?)?;
writer.write_quad(&q?)?;
Some((writer, quads))
} else {
writer.finish()?;
@ -1227,17 +1234,17 @@ fn evaluate_sparql_query(
.with_body(body))
}
QueryResults::Graph(triples) => {
let format = graph_content_negotiation(request)?;
let format = rdf_content_negotiation(request)?;
ReadForWrite::build_response(
move |w| {
Ok((
GraphSerializer::from_format(format).triple_writer(w),
RdfSerializer::from_format(format).serialize_to_write(w),
triples,
))
},
|(mut writer, mut triples)| {
Ok(if let Some(t) = triples.next() {
writer.write(&t?)?;
writer.write_triple(&t?)?;
Some((writer, triples))
} else {
writer.finish()?;
@ -1403,26 +1410,26 @@ impl From<NamedGraphName> for GraphName {
}
}
fn graph_content_negotiation(request: &Request) -> Result<GraphFormat, HttpError> {
content_negotiation(
request,
&[
GraphFormat::NTriples.media_type(),
GraphFormat::Turtle.media_type(),
GraphFormat::RdfXml.media_type(),
],
GraphFormat::from_media_type,
)
}
fn dataset_content_negotiation(request: &Request) -> Result<DatasetFormat, HttpError> {
fn rdf_content_negotiation(request: &Request) -> Result<RdfFormat, HttpError> {
content_negotiation(
request,
&[
DatasetFormat::NQuads.media_type(),
DatasetFormat::TriG.media_type(),
"application/n-quads",
"application/n-triples",
"application/rdf+xml",
"application/trig",
"application/turtle",
"application/xml",
"application/x-trig",
"application/x-turtle",
"text/n3",
"text/nquads",
"text/plain",
"text/turtle",
"text/xml",
"text/x-nquads",
],
DatasetFormat::from_media_type,
RdfFormat::from_media_type,
)
}
@ -1430,10 +1437,15 @@ fn query_results_content_negotiation(request: &Request) -> Result<QueryResultsFo
content_negotiation(
request,
&[
QueryResultsFormat::Json.media_type(),
QueryResultsFormat::Xml.media_type(),
QueryResultsFormat::Csv.media_type(),
QueryResultsFormat::Tsv.media_type(),
"application/json",
"application/sparql-results+json",
"application/sparql-results+xml",
"application/xml",
"text/csv",
"text/json",
"text/tab-separated-values",
"text/tsv",
"text/xml",
],
QueryResultsFormat::from_media_type,
)

Loading…
Cancel
Save