Makes sparesults formatter API closer to oxrdfio

pull/635/head
Tpt 1 year ago committed by Thomas Tanon
parent 0783d1dcda
commit 6a21cb0625
  1. 19
      cli/src/main.rs
  2. 4
      fuzz/src/result_format.rs
  3. 8
      lib/sparesults/README.md
  4. 2
      lib/sparesults/src/lib.rs
  5. 84
      lib/sparesults/src/serializer.rs
  6. 9
      lib/src/sparql/model.rs
  7. 8
      lib/src/sparql/results.rs
  8. 4
      python/src/sparql.rs

@ -555,7 +555,7 @@ pub fn main() -> anyhow::Result<()> {
}; };
if let Some(results_file) = results_file { if let Some(results_file) = results_file {
let mut writer = QueryResultsSerializer::from_format(format) let mut writer = QueryResultsSerializer::from_format(format)
.solutions_writer( .serialize_solutions_to_write(
BufWriter::new(File::create(results_file)?), BufWriter::new(File::create(results_file)?),
solutions.variables().to_vec(), solutions.variables().to_vec(),
)?; )?;
@ -565,7 +565,7 @@ pub fn main() -> anyhow::Result<()> {
close_file_writer(writer.finish()?)?; close_file_writer(writer.finish()?)?;
} else { } else {
let mut writer = QueryResultsSerializer::from_format(format) let mut writer = QueryResultsSerializer::from_format(format)
.solutions_writer( .serialize_solutions_to_write(
stdout().lock(), stdout().lock(),
solutions.variables().to_vec(), solutions.variables().to_vec(),
)?; )?;
@ -595,14 +595,15 @@ pub fn main() -> anyhow::Result<()> {
}; };
if let Some(results_file) = results_file { if let Some(results_file) = results_file {
close_file_writer( close_file_writer(
QueryResultsSerializer::from_format(format).write_boolean_result( QueryResultsSerializer::from_format(format)
BufWriter::new(File::create(results_file)?), .serialize_boolean_to_write(
result, BufWriter::new(File::create(results_file)?),
)?, result,
)?,
)?; )?;
} else { } else {
QueryResultsSerializer::from_format(format) QueryResultsSerializer::from_format(format)
.write_boolean_result(stdout().lock(), result)? .serialize_boolean_to_write(stdout().lock(), result)?
.flush()?; .flush()?;
} }
} }
@ -1318,7 +1319,7 @@ fn evaluate_sparql_query(
move |w| { move |w| {
Ok(( Ok((
QueryResultsSerializer::from_format(format) QueryResultsSerializer::from_format(format)
.solutions_writer(w, solutions.variables().to_vec())?, .serialize_solutions_to_write(w, solutions.variables().to_vec())?,
solutions, solutions,
)) ))
}, },
@ -1338,7 +1339,7 @@ fn evaluate_sparql_query(
let format = query_results_content_negotiation(request)?; let format = query_results_content_negotiation(request)?;
let mut body = Vec::new(); let mut body = Vec::new();
QueryResultsSerializer::from_format(format) QueryResultsSerializer::from_format(format)
.write_boolean_result(&mut body, result) .serialize_boolean_to_write(&mut body, result)
.map_err(internal_server_error)?; .map_err(internal_server_error)?;
Ok(Response::builder(Status::OK) Ok(Response::builder(Status::OK)
.with_header(HeaderName::CONTENT_TYPE, format.media_type()) .with_header(HeaderName::CONTENT_TYPE, format.media_type())

@ -18,7 +18,7 @@ pub fn fuzz_result_format(format: QueryResultsFormat, data: &[u8]) {
// We try to write again // We try to write again
let mut writer = serializer let mut writer = serializer
.solutions_writer( .serialize_solutions_to_write(
Vec::new(), Vec::new(),
solutions solutions
.get(0) .get(0)
@ -49,7 +49,7 @@ pub fn fuzz_result_format(format: QueryResultsFormat, data: &[u8]) {
// We try to write again // We try to write again
let mut serialized = Vec::new(); let mut serialized = Vec::new();
serializer serializer
.write_boolean_result(&mut serialized, value) .serialize_boolean_to_write(&mut serialized, value)
.unwrap(); .unwrap();
// And to parse again // And to parse again

@ -28,15 +28,15 @@ fn convert_json_to_tsv(json_file: &[u8]) -> Result<Vec<u8>> {
match json_parser.read_results(json_file)? { match json_parser.read_results(json_file)? {
QueryResultsReader::Boolean(value) => { QueryResultsReader::Boolean(value) => {
// it's a boolean result, we copy it in TSV to the output buffer // it's a boolean result, we copy it in TSV to the output buffer
tsv_serializer.write_boolean_result(Vec::new(), value) tsv_serializer.serialize_boolean_to_write(Vec::new(), value)
}, },
QueryResultsReader::Solutions(solutions_reader) => { QueryResultsReader::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 // 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 solutions_writer = tsv_serializer.solutions_writer(Vec::new(), solutions_reader.variables().to_vec())?; let mut serialize_solutions_to_write = tsv_serializer.serialize_solutions_to_write(Vec::new(), solutions_reader.variables().to_vec())?;
for solution in solutions_reader { for solution in solutions_reader {
solutions_writer.write(&solution?)?; serialize_solutions_to_write.write(&solution?)?;
} }
solutions_writer.finish() serialize_solutions_to_write.finish()
} }
} }
} }

@ -16,5 +16,5 @@ mod xml;
pub use crate::error::{ParseError, SyntaxError}; pub use crate::error::{ParseError, SyntaxError};
pub use crate::format::QueryResultsFormat; pub use crate::format::QueryResultsFormat;
pub use crate::parser::{QueryResultsParser, QueryResultsReader, SolutionsReader}; pub use crate::parser::{QueryResultsParser, QueryResultsReader, SolutionsReader};
pub use crate::serializer::{QueryResultsSerializer, SolutionsWriter}; pub use crate::serializer::{QueryResultsSerializer, ToWriteSolutionsWriter};
pub use crate::solution::QuerySolution; pub use crate::solution::QuerySolution;

@ -25,12 +25,12 @@ use std::io::{self, Write};
/// ///
/// // boolean /// // boolean
/// let mut buffer = Vec::new(); /// let mut buffer = Vec::new();
/// json_serializer.write_boolean_result(&mut buffer, true)?; /// json_serializer.serialize_boolean_to_write(&mut buffer, true)?;
/// assert_eq!(buffer, b"{\"head\":{},\"boolean\":true}"); /// assert_eq!(buffer, b"{\"head\":{},\"boolean\":true}");
/// ///
/// // solutions /// // solutions
/// let mut buffer = Vec::new(); /// let mut buffer = Vec::new();
/// let mut writer = json_serializer.solutions_writer(&mut buffer, vec![Variable::new_unchecked("foo"), Variable::new_unchecked("bar")])?; /// let mut writer = json_serializer.serialize_solutions_to_write(&mut buffer, vec![Variable::new_unchecked("foo"), Variable::new_unchecked("bar")])?;
/// writer.write(once((VariableRef::new_unchecked("foo"), LiteralRef::from("test"))))?; /// writer.write(once((VariableRef::new_unchecked("foo"), LiteralRef::from("test"))))?;
/// writer.finish()?; /// writer.finish()?;
/// assert_eq!(buffer, b"{\"head\":{\"vars\":[\"foo\",\"bar\"]},\"results\":{\"bindings\":[{\"foo\":{\"type\":\"literal\",\"value\":\"test\"}}]}}"); /// assert_eq!(buffer, b"{\"head\":{\"vars\":[\"foo\",\"bar\"]},\"results\":{\"bindings\":[{\"foo\":{\"type\":\"literal\",\"value\":\"test\"}}]}}");
@ -55,21 +55,30 @@ impl QueryResultsSerializer {
/// ///
/// let json_serializer = QueryResultsSerializer::from_format(QueryResultsFormat::Xml); /// let json_serializer = QueryResultsSerializer::from_format(QueryResultsFormat::Xml);
/// let mut buffer = Vec::new(); /// let mut buffer = Vec::new();
/// json_serializer.write_boolean_result(&mut buffer, true)?; /// json_serializer.serialize_boolean_to_write(&mut buffer, true)?;
/// assert_eq!(buffer, b"<?xml version=\"1.0\"?><sparql xmlns=\"http://www.w3.org/2005/sparql-results#\"><head></head><boolean>true</boolean></sparql>"); /// assert_eq!(buffer, b"<?xml version=\"1.0\"?><sparql xmlns=\"http://www.w3.org/2005/sparql-results#\"><head></head><boolean>true</boolean></sparql>");
/// # std::io::Result::Ok(()) /// # std::io::Result::Ok(())
/// ``` /// ```
pub fn write_boolean_result<W: Write>(&self, writer: W, value: bool) -> io::Result<W> { pub fn serialize_boolean_to_write<W: Write>(&self, write: W, value: bool) -> io::Result<W> {
match self.format { match self.format {
QueryResultsFormat::Xml => write_boolean_xml_result(writer, value), QueryResultsFormat::Xml => write_boolean_xml_result(write, value),
QueryResultsFormat::Json => write_boolean_json_result(writer, value), QueryResultsFormat::Json => write_boolean_json_result(write, value),
QueryResultsFormat::Csv => write_boolean_csv_result(writer, value), QueryResultsFormat::Csv => write_boolean_csv_result(write, value),
QueryResultsFormat::Tsv => write_boolean_tsv_result(writer, value), QueryResultsFormat::Tsv => write_boolean_tsv_result(write, value),
} }
} }
#[deprecated(note = "Use serialize_boolean_to_write")]
pub fn write_boolean_result<W: Write>(&self, writer: W, value: bool) -> io::Result<W> {
self.serialize_boolean_to_write(writer, value)
}
/// Returns a `SolutionsWriter` allowing writing query solutions into the given [`Write`] implementation. /// Returns a `SolutionsWriter` allowing writing query solutions into the given [`Write`] implementation.
/// ///
/// <div class="warning">Do not forget to run the [`finish`](ToWriteSolutionsWriter::finish()) method to properly write the last bytes of the file.</div>
///
/// <div class="warning">This writer does unbuffered writes. You might want to use [`BufWriter`](io::BufWriter) to avoid that.</div>
///
/// Example in XML (the API is the same for JSON and TSV): /// Example in XML (the API is the same for JSON and TSV):
/// ``` /// ```
/// use sparesults::{QueryResultsFormat, QueryResultsSerializer}; /// use sparesults::{QueryResultsFormat, QueryResultsSerializer};
@ -78,40 +87,51 @@ impl QueryResultsSerializer {
/// ///
/// let json_serializer = QueryResultsSerializer::from_format(QueryResultsFormat::Xml); /// let json_serializer = QueryResultsSerializer::from_format(QueryResultsFormat::Xml);
/// let mut buffer = Vec::new(); /// let mut buffer = Vec::new();
/// let mut writer = json_serializer.solutions_writer(&mut buffer, vec![Variable::new_unchecked("foo"), Variable::new_unchecked("bar")])?; /// let mut writer = json_serializer.serialize_solutions_to_write(&mut buffer, vec![Variable::new_unchecked("foo"), Variable::new_unchecked("bar")])?;
/// writer.write(once((VariableRef::new_unchecked("foo"), LiteralRef::from("test"))))?; /// writer.write(once((VariableRef::new_unchecked("foo"), LiteralRef::from("test"))))?;
/// writer.finish()?; /// writer.finish()?;
/// assert_eq!(buffer, b"<?xml version=\"1.0\"?><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>"); /// assert_eq!(buffer, b"<?xml version=\"1.0\"?><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>");
/// # std::io::Result::Ok(()) /// # std::io::Result::Ok(())
/// ``` /// ```
pub fn solutions_writer<W: Write>( pub fn serialize_solutions_to_write<W: Write>(
&self, &self,
writer: W, write: W,
variables: Vec<Variable>, variables: Vec<Variable>,
) -> io::Result<SolutionsWriter<W>> { ) -> io::Result<ToWriteSolutionsWriter<W>> {
Ok(SolutionsWriter { Ok(ToWriteSolutionsWriter {
formatter: match self.format { formatter: match self.format {
QueryResultsFormat::Xml => { QueryResultsFormat::Xml => {
SolutionsWriterKind::Xml(XmlSolutionsWriter::start(writer, &variables)?) ToWriteSolutionsWriterKind::Xml(XmlSolutionsWriter::start(write, &variables)?)
} }
QueryResultsFormat::Json => { QueryResultsFormat::Json => {
SolutionsWriterKind::Json(JsonSolutionsWriter::start(writer, &variables)?) ToWriteSolutionsWriterKind::Json(JsonSolutionsWriter::start(write, &variables)?)
} }
QueryResultsFormat::Csv => { QueryResultsFormat::Csv => {
SolutionsWriterKind::Csv(CsvSolutionsWriter::start(writer, variables)?) ToWriteSolutionsWriterKind::Csv(CsvSolutionsWriter::start(write, variables)?)
} }
QueryResultsFormat::Tsv => { QueryResultsFormat::Tsv => {
SolutionsWriterKind::Tsv(TsvSolutionsWriter::start(writer, variables)?) ToWriteSolutionsWriterKind::Tsv(TsvSolutionsWriter::start(write, variables)?)
} }
}, },
}) })
} }
#[deprecated(note = "Use serialize_solutions_to_write")]
pub fn solutions_writer<W: Write>(
&self,
writer: W,
variables: Vec<Variable>,
) -> io::Result<ToWriteSolutionsWriter<W>> {
self.serialize_solutions_to_write(writer, variables)
}
} }
/// Allows writing query results. /// Allows writing query results.
/// Could be built using a [`QueryResultsSerializer`]. /// Could be built using a [`QueryResultsSerializer`].
/// ///
/// <div class="warning">Do not forget to run the [`finish`](SolutionsWriter::finish()) method to properly write the last bytes of the file.</div> /// <div class="warning">Do not forget to run the [`finish`](ToWriteSolutionsWriter::finish()) method to properly write the last bytes of the file.</div>
///
/// <div class="warning">This writer does unbuffered writes. You might want to use [`BufWriter`](io::BufWriter) to avoid that.</div>
/// ///
/// Example in TSV (the API is the same for JSON and XML): /// Example in TSV (the API is the same for JSON and XML):
/// ``` /// ```
@ -121,25 +141,25 @@ impl QueryResultsSerializer {
/// ///
/// let json_serializer = QueryResultsSerializer::from_format(QueryResultsFormat::Tsv); /// let json_serializer = QueryResultsSerializer::from_format(QueryResultsFormat::Tsv);
/// let mut buffer = Vec::new(); /// let mut buffer = Vec::new();
/// let mut writer = json_serializer.solutions_writer(&mut buffer, vec![Variable::new_unchecked("foo"), Variable::new_unchecked("bar")])?; /// let mut writer = json_serializer.serialize_solutions_to_write(&mut buffer, vec![Variable::new_unchecked("foo"), Variable::new_unchecked("bar")])?;
/// writer.write(once((VariableRef::new_unchecked("foo"), LiteralRef::from("test"))))?; /// writer.write(once((VariableRef::new_unchecked("foo"), LiteralRef::from("test"))))?;
/// writer.finish()?; /// writer.finish()?;
/// assert_eq!(buffer, b"?foo\t?bar\n\"test\"\t\n"); /// assert_eq!(buffer, b"?foo\t?bar\n\"test\"\t\n");
/// # std::io::Result::Ok(()) /// # std::io::Result::Ok(())
/// ``` /// ```
#[must_use] #[must_use]
pub struct SolutionsWriter<W: Write> { pub struct ToWriteSolutionsWriter<W: Write> {
formatter: SolutionsWriterKind<W>, formatter: ToWriteSolutionsWriterKind<W>,
} }
enum SolutionsWriterKind<W: Write> { enum ToWriteSolutionsWriterKind<W: Write> {
Xml(XmlSolutionsWriter<W>), Xml(XmlSolutionsWriter<W>),
Json(JsonSolutionsWriter<W>), Json(JsonSolutionsWriter<W>),
Csv(CsvSolutionsWriter<W>), Csv(CsvSolutionsWriter<W>),
Tsv(TsvSolutionsWriter<W>), Tsv(TsvSolutionsWriter<W>),
} }
impl<W: Write> SolutionsWriter<W> { impl<W: Write> ToWriteSolutionsWriter<W> {
/// Writes a solution. /// Writes a solution.
/// ///
/// Example in JSON (the API is the same for XML and TSV): /// Example in JSON (the API is the same for XML and TSV):
@ -150,7 +170,7 @@ impl<W: Write> SolutionsWriter<W> {
/// ///
/// let json_serializer = QueryResultsSerializer::from_format(QueryResultsFormat::Json); /// let json_serializer = QueryResultsSerializer::from_format(QueryResultsFormat::Json);
/// let mut buffer = Vec::new(); /// let mut buffer = Vec::new();
/// let mut writer = json_serializer.solutions_writer(&mut buffer, vec![Variable::new_unchecked("foo"), Variable::new_unchecked("bar")])?; /// let mut writer = json_serializer.serialize_solutions_to_write(&mut buffer, vec![Variable::new_unchecked("foo"), Variable::new_unchecked("bar")])?;
/// writer.write(once((VariableRef::new_unchecked("foo"), LiteralRef::from("test"))))?; /// writer.write(once((VariableRef::new_unchecked("foo"), LiteralRef::from("test"))))?;
/// writer.write(&QuerySolution::from((vec![Variable::new_unchecked("bar")], vec![Some(Literal::from("test").into())])))?; /// writer.write(&QuerySolution::from((vec![Variable::new_unchecked("bar")], vec![Some(Literal::from("test").into())])))?;
/// writer.finish()?; /// writer.finish()?;
@ -163,20 +183,20 @@ impl<W: Write> SolutionsWriter<W> {
) -> io::Result<()> { ) -> io::Result<()> {
let solution = solution.into_iter().map(|(v, s)| (v.into(), s.into())); let solution = solution.into_iter().map(|(v, s)| (v.into(), s.into()));
match &mut self.formatter { match &mut self.formatter {
SolutionsWriterKind::Xml(writer) => writer.write(solution), ToWriteSolutionsWriterKind::Xml(writer) => writer.write(solution),
SolutionsWriterKind::Json(writer) => writer.write(solution), ToWriteSolutionsWriterKind::Json(writer) => writer.write(solution),
SolutionsWriterKind::Csv(writer) => writer.write(solution), ToWriteSolutionsWriterKind::Csv(writer) => writer.write(solution),
SolutionsWriterKind::Tsv(writer) => writer.write(solution), ToWriteSolutionsWriterKind::Tsv(writer) => writer.write(solution),
} }
} }
/// Writes the last bytes of the file. /// Writes the last bytes of the file.
pub fn finish(self) -> io::Result<W> { pub fn finish(self) -> io::Result<W> {
match self.formatter { match self.formatter {
SolutionsWriterKind::Xml(write) => write.finish(), ToWriteSolutionsWriterKind::Xml(write) => write.finish(),
SolutionsWriterKind::Json(write) => write.finish(), ToWriteSolutionsWriterKind::Json(write) => write.finish(),
SolutionsWriterKind::Csv(write) => write.finish(), ToWriteSolutionsWriterKind::Csv(write) => write.finish(),
SolutionsWriterKind::Tsv(write) => write.finish(), ToWriteSolutionsWriterKind::Tsv(write) => write.finish(),
} }
} }
} }

@ -58,12 +58,12 @@ impl QueryResults {
match self { match self {
Self::Boolean(value) => { Self::Boolean(value) => {
serializer serializer
.write_boolean_result(writer, value) .serialize_boolean_to_write(writer, value)
.map_err(EvaluationError::ResultsSerialization)?; .map_err(EvaluationError::ResultsSerialization)?;
} }
Self::Solutions(solutions) => { Self::Solutions(solutions) => {
let mut writer = serializer let mut writer = serializer
.solutions_writer(writer, solutions.variables().to_vec()) .serialize_solutions_to_write(writer, solutions.variables().to_vec())
.map_err(EvaluationError::ResultsSerialization)?; .map_err(EvaluationError::ResultsSerialization)?;
for solution in solutions { for solution in solutions {
writer writer
@ -79,7 +79,10 @@ impl QueryResults {
let p = VariableRef::new_unchecked("predicate"); let p = VariableRef::new_unchecked("predicate");
let o = VariableRef::new_unchecked("object"); let o = VariableRef::new_unchecked("object");
let mut writer = serializer let mut writer = serializer
.solutions_writer(writer, vec![s.into_owned(), p.into_owned(), o.into_owned()]) .serialize_solutions_to_write(
writer,
vec![s.into_owned(), p.into_owned(), o.into_owned()],
)
.map_err(EvaluationError::ResultsSerialization)?; .map_err(EvaluationError::ResultsSerialization)?;
for triple in triples { for triple in triples {
let triple = triple?; let triple = triple?;

@ -15,15 +15,15 @@
//! match json_parser.read_results(json_file)? { //! match json_parser.read_results(json_file)? {
//! QueryResultsReader::Boolean(value) => { //! QueryResultsReader::Boolean(value) => {
//! // it's a boolean result, we copy it in TSV to the output buffer //! // it's a boolean result, we copy it in TSV to the output buffer
//! tsv_serializer.write_boolean_result(Vec::new(), value) //! tsv_serializer.serialize_boolean_to_write(Vec::new(), value)
//! }, //! },
//! QueryResultsReader::Solutions(solutions_reader) => { //! QueryResultsReader::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 //! // 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 solutions_writer = tsv_serializer.solutions_writer(Vec::new(), solutions_reader.variables().to_vec())?; //! let mut serialize_solutions_to_write = tsv_serializer.serialize_solutions_to_write(Vec::new(), solutions_reader.variables().to_vec())?;
//! for solution in solutions_reader { //! for solution in solutions_reader {
//! solutions_writer.write(&solution?)?; //! serialize_solutions_to_write.write(&solution?)?;
//! } //! }
//! solutions_writer.finish() //! serialize_solutions_to_write.finish()
//! } //! }
//! } //! }
//! } //! }

@ -247,7 +247,7 @@ impl PyQuerySolutions {
PyWritable::do_write( PyWritable::do_write(
|output, format| { |output, format| {
let mut writer = QueryResultsSerializer::from_format(format) let mut writer = QueryResultsSerializer::from_format(format)
.solutions_writer( .serialize_solutions_to_write(
output, output,
match &self.inner { match &self.inner {
PyQuerySolutionsVariant::Query(inner) => inner.variables().to_vec(), PyQuerySolutionsVariant::Query(inner) => inner.variables().to_vec(),
@ -349,7 +349,7 @@ impl PyQueryBoolean {
|output, format| { |output, format| {
py.allow_threads(|| { py.allow_threads(|| {
QueryResultsSerializer::from_format(format) QueryResultsSerializer::from_format(format)
.write_boolean_result(output, self.inner) .serialize_boolean_to_write(output, self.inner)
.map_err(map_io_err) .map_err(map_io_err)
}) })
}, },

Loading…
Cancel
Save