use crate::csv::{TsvQueryResultsReader, TsvSolutionsReader}; use crate::error::{ParseError, SyntaxError}; use crate::format::QueryResultsFormat; use crate::json::{JsonQueryResultsReader, JsonSolutionsReader}; use crate::solution::QuerySolution; use crate::xml::{XmlQueryResultsReader, XmlSolutionsReader}; use oxrdf::Variable; use std::io::Read; use std::sync::Arc; /// Parsers for [SPARQL query](https://www.w3.org/TR/sparql11-query/) results serialization formats. /// /// It currently supports the following formats: /// * [SPARQL Query Results XML Format](https://www.w3.org/TR/rdf-sparql-XMLres/) ([`QueryResultsFormat::Xml`](QueryResultsFormat::Xml)). /// * [SPARQL Query Results JSON Format](https://www.w3.org/TR/sparql11-results-json/) ([`QueryResultsFormat::Json`](QueryResultsFormat::Json)). /// * [SPARQL Query Results TSV Format](https://www.w3.org/TR/sparql11-results-csv-tsv/) ([`QueryResultsFormat::Tsv`](QueryResultsFormat::Tsv)). /// /// Example in JSON (the API is the same for XML and TSV): /// ``` /// use sparesults::{QueryResultsFormat, QueryResultsParser, FromReadQueryResultsReader}; /// use oxrdf::{Literal, Variable}; /// /// let json_parser = QueryResultsParser::from_format(QueryResultsFormat::Json); /// // boolean /// if let FromReadQueryResultsReader::Boolean(v) = json_parser.parse_read(b"{\"boolean\":true}".as_slice())? { /// assert_eq!(v, true); /// } /// // solutions /// 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())]); /// } /// } /// # Result::<(),sparesults::ParseError>::Ok(()) /// ``` pub struct QueryResultsParser { format: QueryResultsFormat, } impl QueryResultsParser { /// Builds a parser for the given format. #[inline] pub fn from_format(format: QueryResultsFormat) -> Self { Self { format } } /// Reads a result file. /// /// Reads are buffered. /// /// Example in XML (the API is the same for JSON and TSV): /// ``` /// use sparesults::{QueryResultsFormat, QueryResultsParser, FromReadQueryResultsReader}; /// use oxrdf::{Literal, Variable}; /// /// let json_parser = QueryResultsParser::from_format(QueryResultsFormat::Xml); /// /// // boolean /// 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 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())]); /// } /// } /// # Result::<(),sparesults::ParseError>::Ok(()) /// ``` pub fn parse_read<R: Read>( &self, reader: R, ) -> Result<FromReadQueryResultsReader<R>, ParseError> { Ok(match self.format { QueryResultsFormat::Xml => match XmlQueryResultsReader::read(reader)? { XmlQueryResultsReader::Boolean(r) => FromReadQueryResultsReader::Boolean(r), XmlQueryResultsReader::Solutions { solutions, variables, } => FromReadQueryResultsReader::Solutions(FromReadSolutionsReader { variables: variables.into(), solutions: SolutionsReaderKind::Xml(solutions), }), }, QueryResultsFormat::Json => match JsonQueryResultsReader::read(reader)? { JsonQueryResultsReader::Boolean(r) => FromReadQueryResultsReader::Boolean(r), JsonQueryResultsReader::Solutions { solutions, variables, } => FromReadQueryResultsReader::Solutions(FromReadSolutionsReader { variables: variables.into(), 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) => FromReadQueryResultsReader::Boolean(r), TsvQueryResultsReader::Solutions { solutions, variables, } => FromReadQueryResultsReader::Solutions(FromReadSolutionsReader { variables: variables.into(), solutions: SolutionsReaderKind::Tsv(solutions), }), }, }) } #[deprecated(note = "Use parse_read")] pub fn read_results<R: Read>( &self, reader: R, ) -> Result<FromReadQueryResultsReader<R>, ParseError> { self.parse_read(reader) } } impl From<QueryResultsFormat> for QueryResultsParser { fn from(format: QueryResultsFormat) -> Self { Self::from_format(format) } } /// 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 ([`FromReadSolutionsReader`]). /// /// Example in TSV (the API is the same for JSON and XML): /// ``` /// use sparesults::{QueryResultsFormat, QueryResultsParser, FromReadQueryResultsReader}; /// use oxrdf::{Literal, Variable}; /// /// let json_parser = QueryResultsParser::from_format(QueryResultsFormat::Tsv); /// /// // boolean /// if let FromReadQueryResultsReader::Boolean(v) = json_parser.parse_read(b"true".as_slice())? { /// assert_eq!(v, true); /// } /// /// // solutions /// 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())]); /// } /// } /// # Result::<(),sparesults::ParseError>::Ok(()) /// ``` pub enum FromReadQueryResultsReader<R: Read> { Solutions(FromReadSolutionsReader<R>), Boolean(bool), } /// A streaming reader of a set of [`QuerySolution`] solutions. /// /// It implements the [`Iterator`] API to iterate over the solutions. /// /// Example in JSON (the API is the same for XML and TSV): /// ``` /// use sparesults::{QueryResultsFormat, QueryResultsParser, FromReadQueryResultsReader}; /// use oxrdf::{Literal, Variable}; /// /// let json_parser = QueryResultsParser::from_format(QueryResultsFormat::Json); /// 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())]); /// } /// } /// # Result::<(),sparesults::ParseError>::Ok(()) /// ``` pub struct FromReadSolutionsReader<R: Read> { variables: Arc<[Variable]>, solutions: SolutionsReaderKind<R>, } enum SolutionsReaderKind<R: Read> { Xml(XmlSolutionsReader<R>), Json(JsonSolutionsReader<R>), Tsv(TsvSolutionsReader<R>), } impl<R: Read> 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, FromReadQueryResultsReader}; /// use oxrdf::Variable; /// /// let json_parser = QueryResultsParser::from_format(QueryResultsFormat::Tsv); /// 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(()) /// ``` #[inline] pub fn variables(&self) -> &[Variable] { &self.variables } } impl<R: Read> Iterator for FromReadSolutionsReader<R> { type Item = Result<QuerySolution, ParseError>; fn next(&mut self) -> Option<Result<QuerySolution, ParseError>> { Some( match &mut self.solutions { SolutionsReaderKind::Xml(reader) => reader.read_next(), SolutionsReaderKind::Json(reader) => reader.read_next(), SolutionsReaderKind::Tsv(reader) => reader.read_next(), } .transpose()? .map(|values| (Arc::clone(&self.variables), values).into()), ) } }