use crate::oxrdf::Variable; use crate::sparesults::csv::{FromReadTsvQueryResultsReader, FromReadTsvSolutionsReader}; #[cfg(feature = "async-tokio")] use crate::sparesults::csv::{ FromTokioAsyncReadTsvQueryResultsReader, FromTokioAsyncReadTsvSolutionsReader, }; use crate::sparesults::error::{QueryResultsParseError, QueryResultsSyntaxError}; use crate::sparesults::format::QueryResultsFormat; use crate::sparesults::json::{FromReadJsonQueryResultsReader, FromReadJsonSolutionsReader}; #[cfg(feature = "async-tokio")] use crate::sparesults::json::{ FromTokioAsyncReadJsonQueryResultsReader, FromTokioAsyncReadJsonSolutionsReader, }; use crate::sparesults::solution::QuerySolution; use crate::sparesults::xml::{FromReadXmlQueryResultsReader, FromReadXmlSolutionsReader}; #[cfg(feature = "async-tokio")] use crate::sparesults::xml::{ FromTokioAsyncReadXmlQueryResultsReader, FromTokioAsyncReadXmlSolutionsReader, }; use std::io::Read; use std::sync::Arc; #[cfg(feature = "async-tokio")] use tokio::io::AsyncRead; /// 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(br#"{"boolean":true}"#.as_slice())? { /// assert_eq!(v, true); /// } /// // solutions /// if let FromReadQueryResultsReader::Solutions(solutions) = json_parser.parse_read(br#"{"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![(&Variable::new_unchecked("foo"), &Literal::from("test").into())]); /// } /// } /// # Result::<(),sparesults::QueryResultsParseError>::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 from a [`Read`] implementation. /// /// Reads are automatically buffered. /// /// Example in XML (the API is the same for JSON and TSV): /// ``` /// use sparesults::{QueryResultsFormat, QueryResultsParser, FromReadQueryResultsReader}; /// use oxrdf::{Literal, Variable}; /// /// let xml_parser = QueryResultsParser::from_format(QueryResultsFormat::Xml); /// /// // boolean /// if let FromReadQueryResultsReader::Boolean(v) = xml_parser.parse_read(br#"true"#.as_slice())? { /// assert_eq!(v, true); /// } /// /// // solutions /// if let FromReadQueryResultsReader::Solutions(solutions) = xml_parser.parse_read(br#"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![(&Variable::new_unchecked("foo"), &Literal::from("test").into())]); /// } /// } /// # Result::<(),sparesults::QueryResultsParseError>::Ok(()) /// ``` pub fn parse_read( &self, reader: R, ) -> Result, QueryResultsParseError> { Ok(match self.format { QueryResultsFormat::Xml => match FromReadXmlQueryResultsReader::read(reader)? { FromReadXmlQueryResultsReader::Boolean(r) => FromReadQueryResultsReader::Boolean(r), FromReadXmlQueryResultsReader::Solutions { solutions, variables, } => FromReadQueryResultsReader::Solutions(FromReadSolutionsReader { variables: variables.into(), solutions: FromReadSolutionsReaderKind::Xml(solutions), }), }, QueryResultsFormat::Json => match FromReadJsonQueryResultsReader::read(reader)? { FromReadJsonQueryResultsReader::Boolean(r) => FromReadQueryResultsReader::Boolean(r), FromReadJsonQueryResultsReader::Solutions { solutions, variables, } => FromReadQueryResultsReader::Solutions(FromReadSolutionsReader { variables: variables.into(), solutions: FromReadSolutionsReaderKind::Json(solutions), }), }, QueryResultsFormat::Csv => return Err(QueryResultsSyntaxError::msg("CSV SPARQL results syntax is lossy and can't be parsed to a proper RDF representation").into()), QueryResultsFormat::Tsv => match FromReadTsvQueryResultsReader::read(reader)? { FromReadTsvQueryResultsReader::Boolean(r) => FromReadQueryResultsReader::Boolean(r), FromReadTsvQueryResultsReader::Solutions { solutions, variables, } => FromReadQueryResultsReader::Solutions(FromReadSolutionsReader { variables: variables.into(), solutions: FromReadSolutionsReaderKind::Tsv(solutions), }), }, }) } #[deprecated(note = "use parse_read", since = "0.4.0")] pub fn read_results( &self, reader: R, ) -> Result, QueryResultsParseError> { self.parse_read(reader) } /// Reads a result file from a Tokio [`AsyncRead`] implementation. /// /// Reads are automatically buffered. /// /// Example in XML (the API is the same for JSON and TSV): /// ``` /// use sparesults::{QueryResultsFormat, QueryResultsParser, FromTokioAsyncReadQueryResultsReader}; /// use oxrdf::{Literal, Variable}; /// /// # #[tokio::main(flavor = "current_thread")] /// # async fn main() -> Result<(), sparesults::QueryResultsParseError> { /// let xml_parser = QueryResultsParser::from_format(QueryResultsFormat::Xml); /// /// // boolean /// if let FromTokioAsyncReadQueryResultsReader::Boolean(v) = xml_parser.parse_tokio_async_read(br#"true"#.as_slice()).await? { /// assert_eq!(v, true); /// } /// /// // solutions /// if let FromTokioAsyncReadQueryResultsReader::Solutions(mut solutions) = xml_parser.parse_tokio_async_read(br#"test"#.as_slice()).await? { /// assert_eq!(solutions.variables(), &[Variable::new_unchecked("foo"), Variable::new_unchecked("bar")]); /// while let Some(solution) = solutions.next().await { /// assert_eq!(solution?.iter().collect::>(), vec![(&Variable::new_unchecked("foo"), &Literal::from("test").into())]); /// } /// } /// # Ok(()) /// # } /// ``` #[cfg(feature = "async-tokio")] pub async fn parse_tokio_async_read( &self, reader: R, ) -> Result, QueryResultsParseError> { Ok(match self.format { QueryResultsFormat::Xml => match FromTokioAsyncReadXmlQueryResultsReader::read(reader).await? { FromTokioAsyncReadXmlQueryResultsReader::Boolean(r) => FromTokioAsyncReadQueryResultsReader::Boolean(r), FromTokioAsyncReadXmlQueryResultsReader::Solutions { solutions, variables, } => FromTokioAsyncReadQueryResultsReader::Solutions(FromTokioAsyncReadSolutionsReader { variables: variables.into(), solutions: FromTokioAsyncReadSolutionsReaderKind::Xml(solutions), }), }, QueryResultsFormat::Json => match FromTokioAsyncReadJsonQueryResultsReader::read(reader).await? { FromTokioAsyncReadJsonQueryResultsReader::Boolean(r) => FromTokioAsyncReadQueryResultsReader::Boolean(r), FromTokioAsyncReadJsonQueryResultsReader::Solutions { solutions, variables, } => FromTokioAsyncReadQueryResultsReader::Solutions(FromTokioAsyncReadSolutionsReader { variables: variables.into(), solutions: FromTokioAsyncReadSolutionsReaderKind::Json(solutions), }), }, QueryResultsFormat::Csv => return Err(QueryResultsSyntaxError::msg("CSV SPARQL results syntax is lossy and can't be parsed to a proper RDF representation").into()), QueryResultsFormat::Tsv => match FromTokioAsyncReadTsvQueryResultsReader::read(reader).await? { FromTokioAsyncReadTsvQueryResultsReader::Boolean(r) => FromTokioAsyncReadQueryResultsReader::Boolean(r), FromTokioAsyncReadTsvQueryResultsReader::Solutions { solutions, variables, } => FromTokioAsyncReadQueryResultsReader::Solutions(FromTokioAsyncReadSolutionsReader { variables: variables.into(), solutions: FromTokioAsyncReadSolutionsReaderKind::Tsv(solutions), }), }, }) } } impl From 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 oxrdf::{Literal, Variable}; /// use sparesults::{FromReadQueryResultsReader, QueryResultsFormat, QueryResultsParser}; /// /// let tsv_parser = QueryResultsParser::from_format(QueryResultsFormat::Tsv); /// /// // boolean /// if let FromReadQueryResultsReader::Boolean(v) = tsv_parser.parse_read(b"true".as_slice())? { /// assert_eq!(v, true); /// } /// /// // solutions /// if let FromReadQueryResultsReader::Solutions(solutions) = /// tsv_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![( /// &Variable::new_unchecked("foo"), /// &Literal::from("test").into() /// )] /// ); /// } /// } /// # Result::<(),sparesults::QueryResultsParseError>::Ok(()) /// ``` pub enum FromReadQueryResultsReader { Solutions(FromReadSolutionsReader), 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(br#"{"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![(&Variable::new_unchecked("foo"), &Literal::from("test").into())]); /// } /// } /// # Result::<(),sparesults::QueryResultsParseError>::Ok(()) /// ``` pub struct FromReadSolutionsReader { variables: Arc<[Variable]>, solutions: FromReadSolutionsReaderKind, } enum FromReadSolutionsReaderKind { Xml(FromReadXmlSolutionsReader), Json(FromReadJsonSolutionsReader), Tsv(FromReadTsvSolutionsReader), } impl FromReadSolutionsReader { /// 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 oxrdf::Variable; /// use sparesults::{FromReadQueryResultsReader, QueryResultsFormat, QueryResultsParser}; /// /// let tsv_parser = QueryResultsParser::from_format(QueryResultsFormat::Tsv); /// if let FromReadQueryResultsReader::Solutions(solutions) = /// tsv_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::QueryResultsParseError>::Ok(()) /// ``` #[inline] pub fn variables(&self) -> &[Variable] { &self.variables } } impl Iterator for FromReadSolutionsReader { type Item = Result; fn next(&mut self) -> Option { Some( match &mut self.solutions { FromReadSolutionsReaderKind::Xml(reader) => reader.read_next(), FromReadSolutionsReaderKind::Json(reader) => reader.read_next(), FromReadSolutionsReaderKind::Tsv(reader) => reader.read_next(), } .transpose()? .map(|values| (Arc::clone(&self.variables), values).into()), ) } } /// 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 oxrdf::{Literal, Variable}; /// use sparesults::{ /// FromTokioAsyncReadQueryResultsReader, QueryResultsFormat, QueryResultsParser, /// }; /// /// # #[tokio::main(flavor = "current_thread")] /// # async fn main() -> Result<(), sparesults::QueryResultsParseError> { /// let tsv_parser = QueryResultsParser::from_format(QueryResultsFormat::Tsv); /// /// // boolean /// if let FromTokioAsyncReadQueryResultsReader::Boolean(v) = tsv_parser /// .parse_tokio_async_read(b"true".as_slice()) /// .await? /// { /// assert_eq!(v, true); /// } /// /// // solutions /// if let FromTokioAsyncReadQueryResultsReader::Solutions(mut solutions) = tsv_parser /// .parse_tokio_async_read(b"?foo\t?bar\n\"test\"\t".as_slice()) /// .await? /// { /// assert_eq!( /// solutions.variables(), /// &[ /// Variable::new_unchecked("foo"), /// Variable::new_unchecked("bar") /// ] /// ); /// while let Some(solution) = solutions.next().await { /// assert_eq!( /// solution?.iter().collect::>(), /// vec![( /// &Variable::new_unchecked("foo"), /// &Literal::from("test").into() /// )] /// ); /// } /// } /// # Ok(()) /// # } /// ``` #[cfg(feature = "async-tokio")] pub enum FromTokioAsyncReadQueryResultsReader { Solutions(FromTokioAsyncReadSolutionsReader), 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, FromTokioAsyncReadQueryResultsReader}; /// use oxrdf::{Literal, Variable}; /// /// # #[tokio::main(flavor = "current_thread")] /// # async fn main() -> Result<(), sparesults::QueryResultsParseError> { /// let json_parser = QueryResultsParser::from_format(QueryResultsFormat::Json); /// if let FromTokioAsyncReadQueryResultsReader::Solutions(mut solutions) = json_parser.parse_tokio_async_read(br#"{"head":{"vars":["foo","bar"]},"results":{"bindings":[{"foo":{"type":"literal","value":"test"}}]}}"#.as_slice()).await? { /// assert_eq!(solutions.variables(), &[Variable::new_unchecked("foo"), Variable::new_unchecked("bar")]); /// while let Some(solution) = solutions.next().await { /// assert_eq!(solution?.iter().collect::>(), vec![(&Variable::new_unchecked("foo"), &Literal::from("test").into())]); /// } /// } /// # Ok(()) /// # } /// ``` #[cfg(feature = "async-tokio")] pub struct FromTokioAsyncReadSolutionsReader { variables: Arc<[Variable]>, solutions: FromTokioAsyncReadSolutionsReaderKind, } #[cfg(feature = "async-tokio")] enum FromTokioAsyncReadSolutionsReaderKind { Json(FromTokioAsyncReadJsonSolutionsReader), Xml(FromTokioAsyncReadXmlSolutionsReader), Tsv(FromTokioAsyncReadTsvSolutionsReader), } #[cfg(feature = "async-tokio")] impl FromTokioAsyncReadSolutionsReader { /// 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 oxrdf::Variable; /// use sparesults::{ /// FromTokioAsyncReadQueryResultsReader, QueryResultsFormat, QueryResultsParser, /// }; /// /// # #[tokio::main(flavor = "current_thread")] /// # async fn main() -> Result<(), sparesults::QueryResultsParseError> { /// let tsv_parser = QueryResultsParser::from_format(QueryResultsFormat::Tsv); /// if let FromTokioAsyncReadQueryResultsReader::Solutions(solutions) = tsv_parser /// .parse_tokio_async_read(b"?foo\t?bar\n\"ex1\"\t\"ex2\"".as_slice()) /// .await? /// { /// assert_eq!( /// solutions.variables(), /// &[ /// Variable::new_unchecked("foo"), /// Variable::new_unchecked("bar") /// ] /// ); /// } /// # Ok(()) /// # } /// ``` #[inline] pub fn variables(&self) -> &[Variable] { &self.variables } /// Reads the next solution or returns `None` if the file is finished. pub async fn next(&mut self) -> Option> { Some( match &mut self.solutions { FromTokioAsyncReadSolutionsReaderKind::Json(reader) => reader.read_next().await, FromTokioAsyncReadSolutionsReaderKind::Xml(reader) => reader.read_next().await, FromTokioAsyncReadSolutionsReaderKind::Tsv(reader) => reader.read_next().await, } .transpose()? .map(|values| (Arc::clone(&self.variables), values).into()), ) } }