Makes sparesults parser API closer to oxrdfio

pull/635/head
Tpt 1 year ago committed by Thomas Tanon
parent 7a3e07d98d
commit 412ca37b3c
  1. 16
      fuzz/src/result_format.rs
  2. 8
      lib/sparesults/README.md
  3. 2
      lib/sparesults/src/lib.rs
  4. 63
      lib/sparesults/src/parser.rs
  5. 18
      lib/src/sparql/model.rs
  6. 14
      lib/src/sparql/results.rs
  7. 11
      python/src/sparql.rs

@ -1,17 +1,17 @@
use anyhow::Context;
use sparesults::{
QueryResultsFormat, QueryResultsParser, QueryResultsReader, QueryResultsSerializer,
FromReadQueryResultsReader, QueryResultsFormat, QueryResultsParser, QueryResultsSerializer,
};
pub fn fuzz_result_format(format: QueryResultsFormat, data: &[u8]) {
let parser = QueryResultsParser::from_format(format);
let serializer = QueryResultsSerializer::from_format(format);
let Ok(reader) = parser.read_results(data) else {
let Ok(reader) = parser.parse_read(data) else {
return;
};
match reader {
QueryResultsReader::Solutions(solutions) => {
FromReadQueryResultsReader::Solutions(solutions) => {
let Ok(solutions) = solutions.collect::<Result<Vec<_>, _>>() else {
return;
};
@ -31,8 +31,8 @@ pub fn fuzz_result_format(format: QueryResultsFormat, data: &[u8]) {
let serialized = String::from_utf8(writer.finish().unwrap()).unwrap();
// And to parse again
if let QueryResultsReader::Solutions(roundtrip_solutions) = parser
.read_results(serialized.as_bytes())
if let FromReadQueryResultsReader::Solutions(roundtrip_solutions) = parser
.parse_read(serialized.as_bytes())
.with_context(|| format!("Parsing {:?}", &serialized))
.unwrap()
{
@ -45,7 +45,7 @@ pub fn fuzz_result_format(format: QueryResultsFormat, data: &[u8]) {
)
}
}
QueryResultsReader::Boolean(value) => {
FromReadQueryResultsReader::Boolean(value) => {
// We try to write again
let mut serialized = Vec::new();
serializer
@ -53,8 +53,8 @@ pub fn fuzz_result_format(format: QueryResultsFormat, data: &[u8]) {
.unwrap();
// And to parse again
if let QueryResultsReader::Boolean(roundtrip_value) =
parser.read_results(serialized.as_slice()).unwrap()
if let FromReadQueryResultsReader::Boolean(roundtrip_value) =
parser.parse_read(serialized.as_slice()).unwrap()
{
assert_eq!(roundtrip_value, value)
}

@ -18,19 +18,19 @@ This crate is intended to be a building piece for SPARQL client and server imple
Usage example converting a JSON result file into a TSV result file:
```rust
use sparesults::{QueryResultsFormat, QueryResultsParser, QueryResultsReader, QueryResultsSerializer};
use sparesults::{QueryResultsFormat, QueryResultsParser, FromReadQueryResultsReader, QueryResultsSerializer};
use std::io::Result;
fn convert_json_to_tsv(json_file: &[u8]) -> Result<Vec<u8>> {
let json_parser = QueryResultsParser::from_format(QueryResultsFormat::Json);
let tsv_serializer = QueryResultsSerializer::from_format(QueryResultsFormat::Tsv);
// We start to read the JSON file and see which kind of results it is
match json_parser.read_results(json_file)? {
QueryResultsReader::Boolean(value) => {
match json_parser.parse_read(json_file)? {
FromReadQueryResultsReader::Boolean(value) => {
// it's a boolean result, we copy it in TSV to the output buffer
tsv_serializer.serialize_boolean_to_write(Vec::new(), value)
},
QueryResultsReader::Solutions(solutions_reader) => {
FromReadQueryResultsReader::Solutions(solutions_reader) => {
// it's a set of solutions, we create a writer and we write to it while reading in streaming from the JSON file
let mut serialize_solutions_to_write = tsv_serializer.serialize_solutions_to_write(Vec::new(), solutions_reader.variables().to_vec())?;
for solution in solutions_reader {

@ -15,6 +15,6 @@ mod xml;
pub use crate::error::{ParseError, SyntaxError};
pub use crate::format::QueryResultsFormat;
pub use crate::parser::{QueryResultsParser, QueryResultsReader, SolutionsReader};
pub use crate::parser::{FromReadQueryResultsReader, FromReadSolutionsReader, QueryResultsParser};
pub use crate::serializer::{QueryResultsSerializer, ToWriteSolutionsWriter};
pub use crate::solution::QuerySolution;

@ -17,16 +17,16 @@ use std::rc::Rc;
///
/// Example in JSON (the API is the same for XML and TSV):
/// ```
/// use sparesults::{QueryResultsFormat, QueryResultsParser, QueryResultsReader};
/// use sparesults::{QueryResultsFormat, QueryResultsParser, FromReadQueryResultsReader};
/// use oxrdf::{Literal, Variable};
///
/// let json_parser = QueryResultsParser::from_format(QueryResultsFormat::Json);
/// // boolean
/// if let QueryResultsReader::Boolean(v) = json_parser.read_results(b"{\"boolean\":true}".as_slice())? {
/// if let FromReadQueryResultsReader::Boolean(v) = json_parser.parse_read(b"{\"boolean\":true}".as_slice())? {
/// assert_eq!(v, true);
/// }
/// // solutions
/// if let QueryResultsReader::Solutions(solutions) = json_parser.read_results(b"{\"head\":{\"vars\":[\"foo\",\"bar\"]},\"results\":{\"bindings\":[{\"foo\":{\"type\":\"literal\",\"value\":\"test\"}}]}}".as_slice())? {
/// if let FromReadQueryResultsReader::Solutions(solutions) = json_parser.parse_read(b"{\"head\":{\"vars\":[\"foo\",\"bar\"]},\"results\":{\"bindings\":[{\"foo\":{\"type\":\"literal\",\"value\":\"test\"}}]}}".as_slice())? {
/// assert_eq!(solutions.variables(), &[Variable::new_unchecked("foo"), Variable::new_unchecked("bar")]);
/// for solution in solutions {
/// assert_eq!(solution?.iter().collect::<Vec<_>>(), vec![(&Variable::new_unchecked("foo"), &Literal::from("test").into())]);
@ -49,18 +49,18 @@ impl QueryResultsParser {
///
/// Example in XML (the API is the same for JSON and TSV):
/// ```
/// use sparesults::{QueryResultsFormat, QueryResultsParser, QueryResultsReader};
/// use sparesults::{QueryResultsFormat, QueryResultsParser, FromReadQueryResultsReader};
/// use oxrdf::{Literal, Variable};
///
/// let json_parser = QueryResultsParser::from_format(QueryResultsFormat::Xml);
///
/// // boolean
/// if let QueryResultsReader::Boolean(v) = json_parser.read_results(b"<sparql xmlns=\"http://www.w3.org/2005/sparql-results#\"><head/><boolean>true</boolean></sparql>".as_slice())? {
/// if let FromReadQueryResultsReader::Boolean(v) = json_parser.parse_read(b"<sparql xmlns=\"http://www.w3.org/2005/sparql-results#\"><head/><boolean>true</boolean></sparql>".as_slice())? {
/// assert_eq!(v, true);
/// }
///
/// // solutions
/// if let QueryResultsReader::Solutions(solutions) = json_parser.read_results(b"<sparql xmlns=\"http://www.w3.org/2005/sparql-results#\"><head><variable name=\"foo\"/><variable name=\"bar\"/></head><results><result><binding name=\"foo\"><literal>test</literal></binding></result></results></sparql>".as_slice())? {
/// if let FromReadQueryResultsReader::Solutions(solutions) = json_parser.parse_read(b"<sparql xmlns=\"http://www.w3.org/2005/sparql-results#\"><head><variable name=\"foo\"/><variable name=\"bar\"/></head><results><result><binding name=\"foo\"><literal>test</literal></binding></result></results></sparql>".as_slice())? {
/// assert_eq!(solutions.variables(), &[Variable::new_unchecked("foo"), Variable::new_unchecked("bar")]);
/// for solution in solutions {
/// assert_eq!(solution?.iter().collect::<Vec<_>>(), vec![(&Variable::new_unchecked("foo"), &Literal::from("test").into())]);
@ -68,61 +68,72 @@ impl QueryResultsParser {
/// }
/// # Result::<(),sparesults::ParseError>::Ok(())
/// ```
pub fn read_results<R: BufRead>(&self, reader: R) -> Result<QueryResultsReader<R>, ParseError> {
pub fn parse_read<R: BufRead>(
&self,
reader: R,
) -> Result<FromReadQueryResultsReader<R>, ParseError> {
Ok(match self.format {
QueryResultsFormat::Xml => match XmlQueryResultsReader::read(reader)? {
XmlQueryResultsReader::Boolean(r) => QueryResultsReader::Boolean(r),
XmlQueryResultsReader::Boolean(r) => FromReadQueryResultsReader::Boolean(r),
XmlQueryResultsReader::Solutions {
solutions,
variables,
} => QueryResultsReader::Solutions(SolutionsReader {
} => FromReadQueryResultsReader::Solutions(FromReadSolutionsReader {
variables: Rc::new(variables),
solutions: SolutionsReaderKind::Xml(solutions),
}),
},
QueryResultsFormat::Json => match JsonQueryResultsReader::read(reader)? {
JsonQueryResultsReader::Boolean(r) => QueryResultsReader::Boolean(r),
JsonQueryResultsReader::Boolean(r) => FromReadQueryResultsReader::Boolean(r),
JsonQueryResultsReader::Solutions {
solutions,
variables,
} => QueryResultsReader::Solutions(SolutionsReader {
} => FromReadQueryResultsReader::Solutions(FromReadSolutionsReader {
variables: Rc::new(variables),
solutions: SolutionsReaderKind::Json(solutions),
}),
},
QueryResultsFormat::Csv => return Err(SyntaxError::msg("CSV SPARQL results syntax is lossy and can't be parsed to a proper RDF representation").into()),
QueryResultsFormat::Tsv => match TsvQueryResultsReader::read(reader)? {
TsvQueryResultsReader::Boolean(r) => QueryResultsReader::Boolean(r),
TsvQueryResultsReader::Boolean(r) => FromReadQueryResultsReader::Boolean(r),
TsvQueryResultsReader::Solutions {
solutions,
variables,
} => QueryResultsReader::Solutions(SolutionsReader {
} => FromReadQueryResultsReader::Solutions(FromReadSolutionsReader {
variables: Rc::new(variables),
solutions: SolutionsReaderKind::Tsv(solutions),
}),
},
})
}
#[deprecated(note = "Use parse_read")]
pub fn read_results<R: BufRead>(
&self,
reader: R,
) -> Result<FromReadQueryResultsReader<R>, ParseError> {
self.parse_read(reader)
}
}
/// The reader for a given read of a results file.
///
/// It is either a read boolean ([`bool`]) or a streaming reader of a set of solutions ([`SolutionsReader`]).
/// It is either a read boolean ([`bool`]) or a streaming reader of a set of solutions ([`FromReadSolutionsReader`]).
///
/// Example in TSV (the API is the same for JSON and XML):
/// ```
/// use sparesults::{QueryResultsFormat, QueryResultsParser, QueryResultsReader};
/// use sparesults::{QueryResultsFormat, QueryResultsParser, FromReadQueryResultsReader};
/// use oxrdf::{Literal, Variable};
///
/// let json_parser = QueryResultsParser::from_format(QueryResultsFormat::Tsv);
///
/// // boolean
/// if let QueryResultsReader::Boolean(v) = json_parser.read_results(b"true".as_slice())? {
/// if let FromReadQueryResultsReader::Boolean(v) = json_parser.parse_read(b"true".as_slice())? {
/// assert_eq!(v, true);
/// }
///
/// // solutions
/// if let QueryResultsReader::Solutions(solutions) = json_parser.read_results(b"?foo\t?bar\n\"test\"\t".as_slice())? {
/// if let FromReadQueryResultsReader::Solutions(solutions) = json_parser.parse_read(b"?foo\t?bar\n\"test\"\t".as_slice())? {
/// assert_eq!(solutions.variables(), &[Variable::new_unchecked("foo"), Variable::new_unchecked("bar")]);
/// for solution in solutions {
/// assert_eq!(solution?.iter().collect::<Vec<_>>(), vec![(&Variable::new_unchecked("foo"), &Literal::from("test").into())]);
@ -130,8 +141,8 @@ impl QueryResultsParser {
/// }
/// # Result::<(),sparesults::ParseError>::Ok(())
/// ```
pub enum QueryResultsReader<R: BufRead> {
Solutions(SolutionsReader<R>),
pub enum FromReadQueryResultsReader<R: BufRead> {
Solutions(FromReadSolutionsReader<R>),
Boolean(bool),
}
@ -141,11 +152,11 @@ pub enum QueryResultsReader<R: BufRead> {
///
/// Example in JSON (the API is the same for XML and TSV):
/// ```
/// use sparesults::{QueryResultsFormat, QueryResultsParser, QueryResultsReader};
/// use sparesults::{QueryResultsFormat, QueryResultsParser, FromReadQueryResultsReader};
/// use oxrdf::{Literal, Variable};
///
/// let json_parser = QueryResultsParser::from_format(QueryResultsFormat::Json);
/// if let QueryResultsReader::Solutions(solutions) = json_parser.read_results(b"{\"head\":{\"vars\":[\"foo\",\"bar\"]},\"results\":{\"bindings\":[{\"foo\":{\"type\":\"literal\",\"value\":\"test\"}}]}}".as_slice())? {
/// if let FromReadQueryResultsReader::Solutions(solutions) = json_parser.parse_read(b"{\"head\":{\"vars\":[\"foo\",\"bar\"]},\"results\":{\"bindings\":[{\"foo\":{\"type\":\"literal\",\"value\":\"test\"}}]}}".as_slice())? {
/// assert_eq!(solutions.variables(), &[Variable::new_unchecked("foo"), Variable::new_unchecked("bar")]);
/// for solution in solutions {
/// assert_eq!(solution?.iter().collect::<Vec<_>>(), vec![(&Variable::new_unchecked("foo"), &Literal::from("test").into())]);
@ -154,7 +165,7 @@ pub enum QueryResultsReader<R: BufRead> {
/// # Result::<(),sparesults::ParseError>::Ok(())
/// ```
#[allow(clippy::rc_buffer)]
pub struct SolutionsReader<R: BufRead> {
pub struct FromReadSolutionsReader<R: BufRead> {
variables: Rc<Vec<Variable>>,
solutions: SolutionsReaderKind<R>,
}
@ -165,16 +176,16 @@ enum SolutionsReaderKind<R: BufRead> {
Tsv(TsvSolutionsReader<R>),
}
impl<R: BufRead> SolutionsReader<R> {
impl<R: BufRead> FromReadSolutionsReader<R> {
/// Ordered list of the declared variables at the beginning of the results.
///
/// Example in TSV (the API is the same for JSON and XML):
/// ```
/// use sparesults::{QueryResultsFormat, QueryResultsParser, QueryResultsReader};
/// use sparesults::{QueryResultsFormat, QueryResultsParser, FromReadQueryResultsReader};
/// use oxrdf::Variable;
///
/// let json_parser = QueryResultsParser::from_format(QueryResultsFormat::Tsv);
/// if let QueryResultsReader::Solutions(solutions) = json_parser.read_results(b"?foo\t?bar\n\"ex1\"\t\"ex2\"".as_slice())? {
/// if let FromReadQueryResultsReader::Solutions(solutions) = json_parser.parse_read(b"?foo\t?bar\n\"ex1\"\t\"ex2\"".as_slice())? {
/// assert_eq!(solutions.variables(), &[Variable::new_unchecked("foo"), Variable::new_unchecked("bar")]);
/// }
/// # Result::<(),sparesults::ParseError>::Ok(())
@ -185,7 +196,7 @@ impl<R: BufRead> SolutionsReader<R> {
}
}
impl<R: BufRead> Iterator for SolutionsReader<R> {
impl<R: BufRead> Iterator for FromReadSolutionsReader<R> {
type Item = Result<QuerySolution, ParseError>;
fn next(&mut self) -> Option<Result<QuerySolution, ParseError>> {

@ -2,8 +2,8 @@ use crate::io::{RdfFormat, RdfSerializer};
use crate::model::*;
use crate::sparql::error::EvaluationError;
use crate::sparql::results::{
ParseError, QueryResultsFormat, QueryResultsParser, QueryResultsReader, QueryResultsSerializer,
SolutionsReader,
FromReadQueryResultsReader, FromReadSolutionsReader, ParseError, QueryResultsFormat,
QueryResultsParser, QueryResultsSerializer,
};
use oxrdf::{Variable, VariableRef};
pub use sparesults::QuerySolution;
@ -27,7 +27,7 @@ impl QueryResults {
format: QueryResultsFormat,
) -> Result<Self, ParseError> {
Ok(QueryResultsParser::from_format(format)
.read_results(reader)?
.parse_read(reader)?
.into())
}
@ -150,11 +150,11 @@ impl From<QuerySolutionIter> for QueryResults {
}
}
impl<R: BufRead + 'static> From<QueryResultsReader<R>> for QueryResults {
fn from(reader: QueryResultsReader<R>) -> Self {
impl<R: BufRead + 'static> From<FromReadQueryResultsReader<R>> for QueryResults {
fn from(reader: FromReadQueryResultsReader<R>) -> Self {
match reader {
QueryResultsReader::Solutions(s) => Self::Solutions(s.into()),
QueryResultsReader::Boolean(v) => Self::Boolean(v),
FromReadQueryResultsReader::Solutions(s) => Self::Solutions(s.into()),
FromReadQueryResultsReader::Boolean(v) => Self::Boolean(v),
}
}
}
@ -211,8 +211,8 @@ impl QuerySolutionIter {
}
}
impl<R: BufRead + 'static> From<SolutionsReader<R>> for QuerySolutionIter {
fn from(reader: SolutionsReader<R>) -> Self {
impl<R: BufRead + 'static> From<FromReadSolutionsReader<R>> for QuerySolutionIter {
fn from(reader: FromReadSolutionsReader<R>) -> Self {
Self {
variables: Rc::new(reader.variables().to_vec()),
iter: Box::new(reader.map(|t| t.map_err(EvaluationError::from))),

@ -5,19 +5,19 @@
//! Usage example converting a JSON result file into a TSV result file:
//!
//! ```
//! use oxigraph::sparql::results::{QueryResultsFormat, QueryResultsParser, QueryResultsReader, QueryResultsSerializer};
//! use oxigraph::sparql::results::{QueryResultsFormat, QueryResultsParser, FromReadQueryResultsReader, QueryResultsSerializer};
//! use std::io::Result;
//!
//! fn convert_json_to_tsv(json_file: &[u8]) -> Result<Vec<u8>> {
//! let json_parser = QueryResultsParser::from_format(QueryResultsFormat::Json);
//! let tsv_serializer = QueryResultsSerializer::from_format(QueryResultsFormat::Tsv);
//! // We start to read the JSON file and see which kind of results it is
//! match json_parser.read_results(json_file)? {
//! QueryResultsReader::Boolean(value) => {
//! match json_parser.parse_read(json_file)? {
//! FromReadQueryResultsReader::Boolean(value) => {
//! // it's a boolean result, we copy it in TSV to the output buffer
//! tsv_serializer.serialize_boolean_to_write(Vec::new(), value)
//! },
//! QueryResultsReader::Solutions(solutions_reader) => {
//! }
//! FromReadQueryResultsReader::Solutions(solutions_reader) => {
//! // it's a set of solutions, we create a writer and we write to it while reading in streaming from the JSON file
//! let mut serialize_solutions_to_write = tsv_serializer.serialize_solutions_to_write(Vec::new(), solutions_reader.variables().to_vec())?;
//! for solution in solutions_reader {
@ -42,6 +42,6 @@
//! ```
pub use sparesults::{
ParseError, QueryResultsFormat, QueryResultsParser, QueryResultsReader, QueryResultsSerializer,
SolutionsReader, SyntaxError,
FromReadQueryResultsReader, FromReadSolutionsReader, ParseError, QueryResultsFormat,
QueryResultsParser, QueryResultsSerializer, SyntaxError,
};

@ -4,7 +4,8 @@ use crate::store::map_storage_error;
use oxigraph::io::RdfSerializer;
use oxigraph::model::Term;
use oxigraph::sparql::results::{
ParseError, QueryResultsParser, QueryResultsReader, QueryResultsSerializer, SolutionsReader,
FromReadQueryResultsReader, FromReadSolutionsReader, ParseError, QueryResultsParser,
QueryResultsSerializer,
};
use oxigraph::sparql::{
EvaluationError, Query, QueryResults, QuerySolution, QuerySolutionIter, QueryTripleIter,
@ -189,7 +190,7 @@ pub struct PyQuerySolutions {
}
enum PyQuerySolutionsVariant {
Query(QuerySolutionIter),
Reader(SolutionsReader<BufReader<PyReadable>>),
Reader(FromReadSolutionsReader<BufReader<PyReadable>>),
}
#[pymethods]
@ -496,14 +497,14 @@ pub fn parse_query_results(
PyReadable::from_data(input)
};
let results = QueryResultsParser::from_format(format)
.read_results(BufReader::new(input))
.parse_read(BufReader::new(input))
.map_err(map_query_results_parse_error)?;
Ok(match results {
QueryResultsReader::Solutions(inner) => PyQuerySolutions {
FromReadQueryResultsReader::Solutions(inner) => PyQuerySolutions {
inner: PyQuerySolutionsVariant::Reader(inner),
}
.into_py(py),
QueryResultsReader::Boolean(inner) => PyQueryBoolean { inner }.into_py(py),
FromReadQueryResultsReader::Boolean(inner) => PyQueryBoolean { inner }.into_py(py),
})
}

Loading…
Cancel
Save