sparesults/
parser.rs

1use crate::csv::{
2    ReaderTsvQueryResultsParserOutput, ReaderTsvSolutionsParser, SliceTsvQueryResultsParserOutput,
3    SliceTsvSolutionsParser,
4};
5#[cfg(feature = "async-tokio")]
6use crate::csv::{TokioAsyncReaderTsvQueryResultsParserOutput, TokioAsyncReaderTsvSolutionsParser};
7use crate::error::{QueryResultsParseError, QueryResultsSyntaxError};
8use crate::format::QueryResultsFormat;
9use crate::json::{
10    ReaderJsonQueryResultsParserOutput, ReaderJsonSolutionsParser,
11    SliceJsonQueryResultsParserOutput, SliceJsonSolutionsParser,
12};
13#[cfg(feature = "async-tokio")]
14use crate::json::{
15    TokioAsyncReaderJsonQueryResultsParserOutput, TokioAsyncReaderJsonSolutionsParser,
16};
17use crate::solution::QuerySolution;
18use crate::xml::{
19    ReaderXmlQueryResultsParserOutput, ReaderXmlSolutionsParser, SliceXmlQueryResultsParserOutput,
20    SliceXmlSolutionsParser,
21};
22#[cfg(feature = "async-tokio")]
23use crate::xml::{TokioAsyncReaderXmlQueryResultsParserOutput, TokioAsyncReaderXmlSolutionsParser};
24use oxrdf::Variable;
25use std::io::Read;
26use std::sync::Arc;
27#[cfg(feature = "async-tokio")]
28use tokio::io::AsyncRead;
29
30/// Parsers for [SPARQL query](https://www.w3.org/TR/sparql11-query/) results serialization formats.
31///
32/// It currently supports the following formats:
33/// * [SPARQL Query Results XML Format](https://www.w3.org/TR/rdf-sparql-XMLres/) ([`QueryResultsFormat::Xml`](QueryResultsFormat::Xml)).
34/// * [SPARQL Query Results JSON Format](https://www.w3.org/TR/sparql11-results-json/) ([`QueryResultsFormat::Json`](QueryResultsFormat::Json)).
35/// * [SPARQL Query Results TSV Format](https://www.w3.org/TR/sparql11-results-csv-tsv/) ([`QueryResultsFormat::Tsv`](QueryResultsFormat::Tsv)).
36///
37/// Example in JSON (the API is the same for XML and TSV):
38/// ```
39/// use sparesults::{QueryResultsFormat, QueryResultsParser, ReaderQueryResultsParserOutput};
40/// use oxrdf::{Literal, Variable};
41///
42/// let json_parser = QueryResultsParser::from_format(QueryResultsFormat::Json);
43/// // boolean
44/// if let ReaderQueryResultsParserOutput::Boolean(v) = json_parser.clone().for_reader(br#"{"boolean":true}"#.as_slice())? {
45///     assert_eq!(v, true);
46/// }
47/// // solutions
48/// if let ReaderQueryResultsParserOutput::Solutions(solutions) = json_parser.for_reader(br#"{"head":{"vars":["foo","bar"]},"results":{"bindings":[{"foo":{"type":"literal","value":"test"}}]}}"#.as_slice())? {
49///     assert_eq!(solutions.variables(), &[Variable::new("foo")?, Variable::new("bar")?]);
50///     for solution in solutions {
51///         assert_eq!(solution?.iter().collect::<Vec<_>>(), vec![(&Variable::new("foo")?, &Literal::from("test").into())]);
52///     }
53/// }
54/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
55/// ```
56#[must_use]
57#[derive(Clone)]
58pub struct QueryResultsParser {
59    format: QueryResultsFormat,
60}
61
62impl QueryResultsParser {
63    /// Builds a parser for the given format.
64    #[inline]
65    pub fn from_format(format: QueryResultsFormat) -> Self {
66        Self { format }
67    }
68
69    /// Reads a result file from a [`Read`] implementation.
70    ///
71    /// Reads are automatically buffered.
72    ///
73    /// Example in XML (the API is the same for JSON and TSV):
74    /// ```
75    /// use sparesults::{QueryResultsFormat, QueryResultsParser, ReaderQueryResultsParserOutput};
76    /// use oxrdf::{Literal, Variable};
77    ///
78    /// let xml_parser = QueryResultsParser::from_format(QueryResultsFormat::Xml);
79    ///
80    /// // boolean
81    /// if let ReaderQueryResultsParserOutput::Boolean(v) = xml_parser.clone().for_reader(br#"<sparql xmlns="http://www.w3.org/2005/sparql-results#"><head/><boolean>true</boolean></sparql>"#.as_slice())? {
82    ///     assert_eq!(v, true);
83    /// }
84    ///
85    /// // solutions
86    /// if let ReaderQueryResultsParserOutput::Solutions(solutions) = xml_parser.for_reader(br#"<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())? {
87    ///     assert_eq!(solutions.variables(), &[Variable::new("foo")?, Variable::new("bar")?]);
88    ///     for solution in solutions {
89    ///         assert_eq!(solution?.iter().collect::<Vec<_>>(), vec![(&Variable::new("foo")?, &Literal::from("test").into())]);
90    ///     }
91    /// }
92    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
93    /// ```
94    pub fn for_reader<R: Read>(
95        self,
96        reader: R,
97    ) -> Result<ReaderQueryResultsParserOutput<R>, QueryResultsParseError> {
98        Ok(match self.format {
99            QueryResultsFormat::Xml => match ReaderXmlQueryResultsParserOutput::read(reader)? {
100                ReaderXmlQueryResultsParserOutput::Boolean(r) => ReaderQueryResultsParserOutput::Boolean(r),
101                ReaderXmlQueryResultsParserOutput::Solutions {
102                    solutions,
103                    variables,
104                } => ReaderQueryResultsParserOutput::Solutions(ReaderSolutionsParser {
105                    variables: variables.into(),
106                    solutions: ReaderSolutionsParserKind::Xml(solutions),
107                }),
108            },
109            QueryResultsFormat::Json => match ReaderJsonQueryResultsParserOutput::read(reader)? {
110                ReaderJsonQueryResultsParserOutput::Boolean(r) => ReaderQueryResultsParserOutput::Boolean(r),
111                ReaderJsonQueryResultsParserOutput::Solutions {
112                    solutions,
113                    variables,
114                } => ReaderQueryResultsParserOutput::Solutions(ReaderSolutionsParser {
115                    variables: variables.into(),
116                    solutions: ReaderSolutionsParserKind::Json(solutions),
117                }),
118            },
119            QueryResultsFormat::Csv => return Err(QueryResultsSyntaxError::msg("CSV SPARQL results syntax is lossy and can't be parsed to a proper RDF representation").into()),
120            QueryResultsFormat::Tsv => match ReaderTsvQueryResultsParserOutput::read(reader)? {
121                ReaderTsvQueryResultsParserOutput::Boolean(r) => ReaderQueryResultsParserOutput::Boolean(r),
122                ReaderTsvQueryResultsParserOutput::Solutions {
123                    solutions,
124                    variables,
125                } => ReaderQueryResultsParserOutput::Solutions(ReaderSolutionsParser {
126                    variables: variables.into(),
127                    solutions: ReaderSolutionsParserKind::Tsv(solutions),
128                }),
129            },
130        })
131    }
132
133    #[deprecated(note = "use for_read", since = "0.4.0")]
134    pub fn read_results<R: Read>(
135        &self,
136        reader: R,
137    ) -> Result<ReaderQueryResultsParserOutput<R>, QueryResultsParseError> {
138        self.clone().for_reader(reader)
139    }
140
141    /// Reads a result file from a Tokio [`AsyncRead`] implementation.
142    ///
143    /// Reads are automatically buffered.
144    ///
145    /// Example in XML (the API is the same for JSON and TSV):
146    /// ```
147    /// # #[tokio::main(flavor = "current_thread")]
148    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
149    /// use sparesults::{QueryResultsFormat, QueryResultsParser, TokioAsyncReaderQueryResultsParserOutput};
150    /// use oxrdf::{Literal, Variable};
151    ///
152    /// let xml_parser = QueryResultsParser::from_format(QueryResultsFormat::Xml);
153    ///
154    /// // boolean
155    /// if let TokioAsyncReaderQueryResultsParserOutput::Boolean(v) = xml_parser.clone().for_tokio_async_reader(br#"<sparql xmlns="http://www.w3.org/2005/sparql-results#"><head/><boolean>true</boolean></sparql>"#.as_slice()).await? {
156    ///     assert_eq!(v, true);
157    /// }
158    ///
159    /// // solutions
160    /// if let TokioAsyncReaderQueryResultsParserOutput::Solutions(mut solutions) = xml_parser.for_tokio_async_reader(br#"<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()).await? {
161    ///     assert_eq!(solutions.variables(), &[Variable::new("foo")?, Variable::new("bar")?]);
162    ///     while let Some(solution) = solutions.next().await {
163    ///         assert_eq!(solution?.iter().collect::<Vec<_>>(), vec![(&Variable::new("foo")?, &Literal::from("test").into())]);
164    ///     }
165    /// }
166    /// # Ok(())
167    /// # }
168    /// ```
169    #[cfg(feature = "async-tokio")]
170    pub async fn for_tokio_async_reader<R: AsyncRead + Unpin>(
171        self,
172        reader: R,
173    ) -> Result<TokioAsyncReaderQueryResultsParserOutput<R>, QueryResultsParseError> {
174        Ok(match self.format {
175            QueryResultsFormat::Xml => match TokioAsyncReaderXmlQueryResultsParserOutput::read(reader).await? {
176                TokioAsyncReaderXmlQueryResultsParserOutput::Boolean(r) => TokioAsyncReaderQueryResultsParserOutput::Boolean(r),
177                TokioAsyncReaderXmlQueryResultsParserOutput::Solutions {
178                    solutions,
179                    variables,
180                } => TokioAsyncReaderQueryResultsParserOutput::Solutions(TokioAsyncReaderSolutionsParser {
181                    variables: variables.into(),
182                    solutions: TokioAsyncReaderSolutionsParserKind::Xml(solutions),
183                }),
184            },
185            QueryResultsFormat::Json => match TokioAsyncReaderJsonQueryResultsParserOutput::read(reader).await? {
186                TokioAsyncReaderJsonQueryResultsParserOutput::Boolean(r) => TokioAsyncReaderQueryResultsParserOutput::Boolean(r),
187                TokioAsyncReaderJsonQueryResultsParserOutput::Solutions {
188                    solutions,
189                    variables,
190                } => TokioAsyncReaderQueryResultsParserOutput::Solutions(TokioAsyncReaderSolutionsParser {
191                    variables: variables.into(),
192                    solutions: TokioAsyncReaderSolutionsParserKind::Json(solutions),
193                }),
194            },
195            QueryResultsFormat::Csv => return Err(QueryResultsSyntaxError::msg("CSV SPARQL results syntax is lossy and can't be parsed to a proper RDF representation").into()),
196            QueryResultsFormat::Tsv => match TokioAsyncReaderTsvQueryResultsParserOutput::read(reader).await? {
197                TokioAsyncReaderTsvQueryResultsParserOutput::Boolean(r) => TokioAsyncReaderQueryResultsParserOutput::Boolean(r),
198                TokioAsyncReaderTsvQueryResultsParserOutput::Solutions {
199                    solutions,
200                    variables,
201                } => TokioAsyncReaderQueryResultsParserOutput::Solutions(TokioAsyncReaderSolutionsParser {
202                    variables: variables.into(),
203                    solutions: TokioAsyncReaderSolutionsParserKind::Tsv(solutions),
204                }),
205            },
206        })
207    }
208
209    /// Reads a result file from a [`Read`] implementation.
210    ///
211    /// Reads are automatically buffered.
212    ///
213    /// Example in XML (the API is the same for JSON and TSV):
214    /// ```
215    /// use sparesults::{QueryResultsFormat, QueryResultsParser, SliceQueryResultsParserOutput};
216    /// use oxrdf::{Literal, Variable};
217    ///
218    /// let xml_parser = QueryResultsParser::from_format(QueryResultsFormat::Xml);
219    ///
220    /// // boolean
221    /// if let SliceQueryResultsParserOutput::Boolean(v) = xml_parser.clone().for_slice(br#"<sparql xmlns="http://www.w3.org/2005/sparql-results#"><head/><boolean>true</boolean></sparql>"#)? {
222    ///     assert_eq!(v, true);
223    /// }
224    ///
225    /// // solutions
226    /// if let SliceQueryResultsParserOutput::Solutions(solutions) = xml_parser.for_slice(br#"<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>"#)? {
227    ///     assert_eq!(solutions.variables(), &[Variable::new("foo")?, Variable::new("bar")?]);
228    ///     for solution in solutions {
229    ///         assert_eq!(solution?.iter().collect::<Vec<_>>(), vec![(&Variable::new("foo")?, &Literal::from("test").into())]);
230    ///     }
231    /// }
232    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
233    /// ```
234    pub fn for_slice(
235        self,
236        slice: &[u8],
237    ) -> Result<SliceQueryResultsParserOutput<'_>, QueryResultsSyntaxError> {
238        Ok(match self.format {
239            QueryResultsFormat::Xml => match SliceXmlQueryResultsParserOutput::read(slice)? {
240                SliceXmlQueryResultsParserOutput::Boolean(r) => SliceQueryResultsParserOutput::Boolean(r),
241                SliceXmlQueryResultsParserOutput::Solutions {
242                    solutions,
243                    variables,
244                } => SliceQueryResultsParserOutput::Solutions(SliceSolutionsParser {
245                    variables: variables.into(),
246                    solutions: SliceSolutionsParserKind::Xml(solutions),
247                }),
248            },
249            QueryResultsFormat::Json => match SliceJsonQueryResultsParserOutput::read(slice)? {
250                SliceJsonQueryResultsParserOutput::Boolean(r) => SliceQueryResultsParserOutput::Boolean(r),
251                SliceJsonQueryResultsParserOutput::Solutions {
252                    solutions,
253                    variables,
254                } => SliceQueryResultsParserOutput::Solutions(SliceSolutionsParser {
255                    variables: variables.into(),
256                    solutions: SliceSolutionsParserKind::Json(solutions),
257                }),
258            },
259            QueryResultsFormat::Csv => return Err(QueryResultsSyntaxError::msg("CSV SPARQL results syntax is lossy and can't be parsed to a proper RDF representation")),
260            QueryResultsFormat::Tsv => match SliceTsvQueryResultsParserOutput::read(slice)? {
261                SliceTsvQueryResultsParserOutput::Boolean(r) => SliceQueryResultsParserOutput::Boolean(r),
262                SliceTsvQueryResultsParserOutput::Solutions {
263                    solutions,
264                    variables,
265                } => SliceQueryResultsParserOutput::Solutions(SliceSolutionsParser {
266                    variables: variables.into(),
267                    solutions: SliceSolutionsParserKind::Tsv(solutions),
268                }),
269            },
270        })
271    }
272}
273
274impl From<QueryResultsFormat> for QueryResultsParser {
275    fn from(format: QueryResultsFormat) -> Self {
276        Self::from_format(format)
277    }
278}
279
280/// The reader for a given read of a results file.
281///
282/// It is either a read boolean ([`bool`]) or a streaming reader of a set of solutions ([`ReaderSolutionsParser`]).
283///
284/// Example in TSV (the API is the same for JSON and XML):
285/// ```
286/// use oxrdf::{Literal, Variable};
287/// use sparesults::{QueryResultsFormat, QueryResultsParser, ReaderQueryResultsParserOutput};
288///
289/// let tsv_parser = QueryResultsParser::from_format(QueryResultsFormat::Tsv);
290///
291/// // boolean
292/// if let ReaderQueryResultsParserOutput::Boolean(v) =
293///     tsv_parser.clone().for_reader(b"true".as_slice())?
294/// {
295///     assert_eq!(v, true);
296/// }
297///
298/// // solutions
299/// if let ReaderQueryResultsParserOutput::Solutions(solutions) =
300///     tsv_parser.for_reader(b"?foo\t?bar\n\"test\"\t".as_slice())?
301/// {
302///     assert_eq!(
303///         solutions.variables(),
304///         &[Variable::new("foo")?, Variable::new("bar")?]
305///     );
306///     for solution in solutions {
307///         assert_eq!(
308///             solution?.iter().collect::<Vec<_>>(),
309///             vec![(&Variable::new("foo")?, &Literal::from("test").into())]
310///         );
311///     }
312/// }
313/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
314/// ```
315#[allow(clippy::large_enum_variant)]
316pub enum ReaderQueryResultsParserOutput<R: Read> {
317    Solutions(ReaderSolutionsParser<R>),
318    Boolean(bool),
319}
320
321/// A streaming parser of a set of [`QuerySolution`] solutions.
322///
323/// It implements the [`Iterator`] API to iterate over the solutions.
324///
325/// Example in JSON (the API is the same for XML and TSV):
326/// ```
327/// use sparesults::{QueryResultsFormat, QueryResultsParser, ReaderQueryResultsParserOutput};
328/// use oxrdf::{Literal, Variable};
329///
330/// let json_parser = QueryResultsParser::from_format(QueryResultsFormat::Json);
331/// if let ReaderQueryResultsParserOutput::Solutions(solutions) = json_parser.for_reader(br#"{"head":{"vars":["foo","bar"]},"results":{"bindings":[{"foo":{"type":"literal","value":"test"}}]}}"#.as_slice())? {
332///     assert_eq!(solutions.variables(), &[Variable::new("foo")?, Variable::new("bar")?]);
333///     for solution in solutions {
334///         assert_eq!(solution?.iter().collect::<Vec<_>>(), vec![(&Variable::new("foo")?, &Literal::from("test").into())]);
335///     }
336/// }
337/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
338/// ```
339pub struct ReaderSolutionsParser<R: Read> {
340    variables: Arc<[Variable]>,
341    solutions: ReaderSolutionsParserKind<R>,
342}
343
344enum ReaderSolutionsParserKind<R: Read> {
345    Xml(ReaderXmlSolutionsParser<R>),
346    Json(ReaderJsonSolutionsParser<R>),
347    Tsv(ReaderTsvSolutionsParser<R>),
348}
349
350impl<R: Read> ReaderSolutionsParser<R> {
351    /// Ordered list of the declared variables at the beginning of the results.
352    ///
353    /// Example in TSV (the API is the same for JSON and XML):
354    /// ```
355    /// use oxrdf::Variable;
356    /// use sparesults::{QueryResultsFormat, QueryResultsParser, ReaderQueryResultsParserOutput};
357    ///
358    /// let tsv_parser = QueryResultsParser::from_format(QueryResultsFormat::Tsv);
359    /// if let ReaderQueryResultsParserOutput::Solutions(solutions) =
360    ///     tsv_parser.for_reader(b"?foo\t?bar\n\"ex1\"\t\"ex2\"".as_slice())?
361    /// {
362    ///     assert_eq!(
363    ///         solutions.variables(),
364    ///         &[Variable::new("foo")?, Variable::new("bar")?]
365    ///     );
366    /// }
367    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
368    /// ```
369    #[inline]
370    pub fn variables(&self) -> &[Variable] {
371        &self.variables
372    }
373}
374
375impl<R: Read> Iterator for ReaderSolutionsParser<R> {
376    type Item = Result<QuerySolution, QueryResultsParseError>;
377
378    fn next(&mut self) -> Option<Self::Item> {
379        Some(
380            match &mut self.solutions {
381                ReaderSolutionsParserKind::Xml(reader) => reader.parse_next(),
382                ReaderSolutionsParserKind::Json(reader) => reader.parse_next(),
383                ReaderSolutionsParserKind::Tsv(reader) => reader.parse_next(),
384            }
385            .transpose()?
386            .map(|values| (Arc::clone(&self.variables), values).into()),
387        )
388    }
389}
390
391/// The reader for a given read of a results file.
392///
393/// It is either a read boolean ([`bool`]) or a streaming reader of a set of solutions ([`ReaderSolutionsParser`]).
394///
395/// Example in TSV (the API is the same for JSON and XML):
396/// ```
397/// # #[tokio::main(flavor = "current_thread")]
398/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
399/// use oxrdf::{Literal, Variable};
400/// use sparesults::{
401///     QueryResultsFormat, QueryResultsParser, TokioAsyncReaderQueryResultsParserOutput,
402/// };
403///
404/// let tsv_parser = QueryResultsParser::from_format(QueryResultsFormat::Tsv);
405///
406/// // boolean
407/// if let TokioAsyncReaderQueryResultsParserOutput::Boolean(v) = tsv_parser
408///     .clone()
409///     .for_tokio_async_reader(b"true".as_slice())
410///     .await?
411/// {
412///     assert_eq!(v, true);
413/// }
414///
415/// // solutions
416/// if let TokioAsyncReaderQueryResultsParserOutput::Solutions(mut solutions) = tsv_parser
417///     .for_tokio_async_reader(b"?foo\t?bar\n\"test\"\t".as_slice())
418///     .await?
419/// {
420///     assert_eq!(
421///         solutions.variables(),
422///         &[Variable::new("foo")?, Variable::new("bar")?]
423///     );
424///     while let Some(solution) = solutions.next().await {
425///         assert_eq!(
426///             solution?.iter().collect::<Vec<_>>(),
427///             vec![(&Variable::new("foo")?, &Literal::from("test").into())]
428///         );
429///     }
430/// }
431/// # Ok(())
432/// # }
433/// ```
434#[cfg(feature = "async-tokio")]
435#[allow(clippy::large_enum_variant)]
436pub enum TokioAsyncReaderQueryResultsParserOutput<R: AsyncRead + Unpin> {
437    Solutions(TokioAsyncReaderSolutionsParser<R>),
438    Boolean(bool),
439}
440
441/// A streaming parser of a set of [`QuerySolution`] solutions.
442///
443/// It implements the [`Iterator`] API to iterate over the solutions.
444///
445/// Example in JSON (the API is the same for XML and TSV):
446/// ```
447/// # #[tokio::main(flavor = "current_thread")]
448/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
449/// use sparesults::{QueryResultsFormat, QueryResultsParser, TokioAsyncReaderQueryResultsParserOutput};
450/// use oxrdf::{Literal, Variable};
451///
452/// let json_parser = QueryResultsParser::from_format(QueryResultsFormat::Json);
453/// if let TokioAsyncReaderQueryResultsParserOutput::Solutions(mut solutions) = json_parser.for_tokio_async_reader(br#"{"head":{"vars":["foo","bar"]},"results":{"bindings":[{"foo":{"type":"literal","value":"test"}}]}}"#.as_slice()).await? {
454///     assert_eq!(solutions.variables(), &[Variable::new("foo")?, Variable::new("bar")?]);
455///     while let Some(solution) = solutions.next().await {
456///         assert_eq!(solution?.iter().collect::<Vec<_>>(), vec![(&Variable::new("foo")?, &Literal::from("test").into())]);
457///     }
458/// }
459/// # Ok(())
460/// # }
461/// ```
462#[cfg(feature = "async-tokio")]
463pub struct TokioAsyncReaderSolutionsParser<R: AsyncRead + Unpin> {
464    variables: Arc<[Variable]>,
465    solutions: TokioAsyncReaderSolutionsParserKind<R>,
466}
467
468#[cfg(feature = "async-tokio")]
469enum TokioAsyncReaderSolutionsParserKind<R: AsyncRead + Unpin> {
470    Json(TokioAsyncReaderJsonSolutionsParser<R>),
471    Xml(TokioAsyncReaderXmlSolutionsParser<R>),
472    Tsv(TokioAsyncReaderTsvSolutionsParser<R>),
473}
474
475#[cfg(feature = "async-tokio")]
476impl<R: AsyncRead + Unpin> TokioAsyncReaderSolutionsParser<R> {
477    /// Ordered list of the declared variables at the beginning of the results.
478    ///
479    /// Example in TSV (the API is the same for JSON and XML):
480    /// ```
481    /// # #[tokio::main(flavor = "current_thread")]
482    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
483    /// use oxrdf::Variable;
484    /// use sparesults::{
485    ///     QueryResultsFormat, QueryResultsParser, TokioAsyncReaderQueryResultsParserOutput,
486    /// };
487    ///
488    /// let tsv_parser = QueryResultsParser::from_format(QueryResultsFormat::Tsv);
489    /// if let TokioAsyncReaderQueryResultsParserOutput::Solutions(solutions) = tsv_parser
490    ///     .for_tokio_async_reader(b"?foo\t?bar\n\"ex1\"\t\"ex2\"".as_slice())
491    ///     .await?
492    /// {
493    ///     assert_eq!(
494    ///         solutions.variables(),
495    ///         &[Variable::new("foo")?, Variable::new("bar")?]
496    ///     );
497    /// }
498    /// # Ok(())
499    /// # }
500    /// ```
501    #[inline]
502    pub fn variables(&self) -> &[Variable] {
503        &self.variables
504    }
505
506    /// Reads the next solution or returns `None` if the file is finished.
507    pub async fn next(&mut self) -> Option<Result<QuerySolution, QueryResultsParseError>> {
508        Some(
509            match &mut self.solutions {
510                TokioAsyncReaderSolutionsParserKind::Json(reader) => reader.parse_next().await,
511                TokioAsyncReaderSolutionsParserKind::Xml(reader) => reader.parse_next().await,
512                TokioAsyncReaderSolutionsParserKind::Tsv(reader) => reader.parse_next().await,
513            }
514            .transpose()?
515            .map(|values| (Arc::clone(&self.variables), values).into()),
516        )
517    }
518}
519
520/// The reader for a given read of a results file.
521///
522/// It is either a read boolean ([`bool`]) or a streaming reader of a set of solutions ([`SliceSolutionsParser`]).
523///
524/// Example in TSV (the API is the same for JSON and XML):
525/// ```
526/// use oxrdf::{Literal, Variable};
527/// use sparesults::{QueryResultsFormat, QueryResultsParser, ReaderQueryResultsParserOutput};
528///
529/// let tsv_parser = QueryResultsParser::from_format(QueryResultsFormat::Tsv);
530///
531/// // boolean
532/// if let ReaderQueryResultsParserOutput::Boolean(v) =
533///     tsv_parser.clone().for_reader(b"true".as_slice())?
534/// {
535///     assert_eq!(v, true);
536/// }
537///
538/// // solutions
539/// if let ReaderQueryResultsParserOutput::Solutions(solutions) =
540///     tsv_parser.for_reader(b"?foo\t?bar\n\"test\"\t".as_slice())?
541/// {
542///     assert_eq!(
543///         solutions.variables(),
544///         &[Variable::new("foo")?, Variable::new("bar")?]
545///     );
546///     for solution in solutions {
547///         assert_eq!(
548///             solution?.iter().collect::<Vec<_>>(),
549///             vec![(&Variable::new("foo")?, &Literal::from("test").into())]
550///         );
551///     }
552/// }
553/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
554/// ```
555#[allow(clippy::large_enum_variant)]
556pub enum SliceQueryResultsParserOutput<'a> {
557    Solutions(SliceSolutionsParser<'a>),
558    Boolean(bool),
559}
560
561/// A streaming parser of a set of [`QuerySolution`] solutions.
562///
563/// It implements the [`Iterator`] API to iterate over the solutions.
564///
565/// Example in JSON (the API is the same for XML and TSV):
566/// ```
567/// use sparesults::{QueryResultsFormat, QueryResultsParser, SliceQueryResultsParserOutput};
568/// use oxrdf::{Literal, Variable};
569///
570/// let json_parser = QueryResultsParser::from_format(QueryResultsFormat::Json);
571/// if let SliceQueryResultsParserOutput::Solutions(solutions) = json_parser.for_slice(br#"{"head":{"vars":["foo","bar"]},"results":{"bindings":[{"foo":{"type":"literal","value":"test"}}]}}"#)? {
572///     assert_eq!(solutions.variables(), &[Variable::new("foo")?, Variable::new("bar")?]);
573///     for solution in solutions {
574///         assert_eq!(solution?.iter().collect::<Vec<_>>(), vec![(&Variable::new("foo")?, &Literal::from("test").into())]);
575///     }
576/// }
577/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
578/// ```
579pub struct SliceSolutionsParser<'a> {
580    variables: Arc<[Variable]>,
581    solutions: SliceSolutionsParserKind<'a>,
582}
583
584enum SliceSolutionsParserKind<'a> {
585    Xml(SliceXmlSolutionsParser<'a>),
586    Json(SliceJsonSolutionsParser<'a>),
587    Tsv(SliceTsvSolutionsParser<'a>),
588}
589
590impl SliceSolutionsParser<'_> {
591    /// Ordered list of the declared variables at the beginning of the results.
592    ///
593    /// Example in TSV (the API is the same for JSON and XML):
594    /// ```
595    /// use oxrdf::Variable;
596    /// use sparesults::{QueryResultsFormat, QueryResultsParser, SliceQueryResultsParserOutput};
597    ///
598    /// let tsv_parser = QueryResultsParser::from_format(QueryResultsFormat::Tsv);
599    /// if let SliceQueryResultsParserOutput::Solutions(solutions) =
600    ///     tsv_parser.for_slice(b"?foo\t?bar\n\"ex1\"\t\"ex2\"")?
601    /// {
602    ///     assert_eq!(
603    ///         solutions.variables(),
604    ///         &[Variable::new("foo")?, Variable::new("bar")?]
605    ///     );
606    /// }
607    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
608    /// ```
609    #[inline]
610    pub fn variables(&self) -> &[Variable] {
611        &self.variables
612    }
613}
614
615impl Iterator for SliceSolutionsParser<'_> {
616    type Item = Result<QuerySolution, QueryResultsSyntaxError>;
617
618    fn next(&mut self) -> Option<Self::Item> {
619        Some(
620            match &mut self.solutions {
621                SliceSolutionsParserKind::Xml(reader) => reader.parse_next(),
622                SliceSolutionsParserKind::Json(reader) => reader.parse_next(),
623                SliceSolutionsParserKind::Tsv(reader) => reader.parse_next(),
624            }
625            .transpose()?
626            .map(|values| (Arc::clone(&self.variables), values).into()),
627        )
628    }
629}