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}