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