sparesults/
serializer.rs

1#[cfg(feature = "async-tokio")]
2use crate::csv::{
3    tokio_async_write_boolean_csv_result, TokioAsyncWriterCsvSolutionsSerializer,
4    TokioAsyncWriterTsvSolutionsSerializer,
5};
6use crate::csv::{
7    write_boolean_csv_result, WriterCsvSolutionsSerializer, WriterTsvSolutionsSerializer,
8};
9use crate::format::QueryResultsFormat;
10#[cfg(feature = "async-tokio")]
11use crate::json::{tokio_async_write_boolean_json_result, TokioAsyncWriterJsonSolutionsSerializer};
12use crate::json::{write_boolean_json_result, WriterJsonSolutionsSerializer};
13#[cfg(feature = "async-tokio")]
14use crate::xml::{tokio_async_write_boolean_xml_result, TokioAsyncWriterXmlSolutionsSerializer};
15use crate::xml::{write_boolean_xml_result, WriterXmlSolutionsSerializer};
16use oxrdf::{TermRef, Variable, VariableRef};
17use std::io::{self, Write};
18#[cfg(feature = "async-tokio")]
19use tokio::io::AsyncWrite;
20
21/// A serializer for [SPARQL query](https://www.w3.org/TR/sparql11-query/) results serialization formats.
22///
23/// It currently supports the following formats:
24/// * [SPARQL Query Results XML Format](https://www.w3.org/TR/rdf-sparql-XMLres/) ([`QueryResultsFormat::Xml`](QueryResultsFormat::Xml))
25/// * [SPARQL Query Results JSON Format](https://www.w3.org/TR/sparql11-results-json/) ([`QueryResultsFormat::Json`](QueryResultsFormat::Json))
26/// * [SPARQL Query Results CSV Format](https://www.w3.org/TR/sparql11-results-csv-tsv/) ([`QueryResultsFormat::Csv`](QueryResultsFormat::Csv))
27/// * [SPARQL Query Results TSV Format](https://www.w3.org/TR/sparql11-results-csv-tsv/) ([`QueryResultsFormat::Tsv`](QueryResultsFormat::Tsv))
28///
29/// Example in JSON (the API is the same for XML, CSV and TSV):
30/// ```
31/// use sparesults::{QueryResultsFormat, QueryResultsSerializer};
32/// use oxrdf::{LiteralRef, Variable, VariableRef};
33/// use std::iter::once;
34///
35/// let json_serializer = QueryResultsSerializer::from_format(QueryResultsFormat::Json);
36///
37/// // boolean
38/// let mut buffer = Vec::new();
39/// json_serializer.clone().serialize_boolean_to_writer(&mut buffer, true)?;
40/// assert_eq!(buffer, br#"{"head":{},"boolean":true}"#);
41///
42/// // solutions
43/// let mut buffer = Vec::new();
44/// let mut serializer = json_serializer.serialize_solutions_to_writer(&mut buffer, vec![Variable::new("foo")?, Variable::new("bar")?])?;
45/// serializer.serialize(once((VariableRef::new("foo")?, LiteralRef::from("test"))))?;
46/// serializer.finish()?;
47/// assert_eq!(buffer, br#"{"head":{"vars":["foo","bar"]},"results":{"bindings":[{"foo":{"type":"literal","value":"test"}}]}}"#);
48/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
49/// ```
50#[must_use]
51#[derive(Clone)]
52pub struct QueryResultsSerializer {
53    format: QueryResultsFormat,
54}
55
56impl QueryResultsSerializer {
57    /// Builds a serializer for the given format.
58    #[inline]
59    pub fn from_format(format: QueryResultsFormat) -> Self {
60        Self { format }
61    }
62
63    /// Write a boolean query result (from an `ASK` query)  into the given [`Write`] implementation.
64    ///
65    /// Example in XML (the API is the same for JSON, CSV and TSV):
66    /// ```
67    /// use sparesults::{QueryResultsFormat, QueryResultsSerializer};
68    ///
69    /// let xml_serializer = QueryResultsSerializer::from_format(QueryResultsFormat::Xml);
70    /// let mut buffer = Vec::new();
71    /// xml_serializer.serialize_boolean_to_writer(&mut buffer, true)?;
72    /// assert_eq!(buffer, br#"<?xml version="1.0"?><sparql xmlns="http://www.w3.org/2005/sparql-results#"><head></head><boolean>true</boolean></sparql>"#);
73    /// # std::io::Result::Ok(())
74    /// ```
75    pub fn serialize_boolean_to_writer<W: Write>(self, writer: W, value: bool) -> io::Result<W> {
76        match self.format {
77            QueryResultsFormat::Xml => write_boolean_xml_result(writer, value),
78            QueryResultsFormat::Json => write_boolean_json_result(writer, value),
79            QueryResultsFormat::Csv | QueryResultsFormat::Tsv => {
80                write_boolean_csv_result(writer, value)
81            }
82        }
83    }
84
85    /// Write a boolean query result (from an `ASK` query)  into the given [`AsyncWrite`] implementation.
86    ///
87    /// Example in JSON (the API is the same for XML, CSV and TSV):
88    /// ```
89    /// # #[tokio::main(flavor = "current_thread")]
90    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
91    /// use sparesults::{QueryResultsFormat, QueryResultsSerializer};
92    ///
93    /// let json_serializer = QueryResultsSerializer::from_format(QueryResultsFormat::Json);
94    /// let mut buffer = Vec::new();
95    /// json_serializer
96    ///     .serialize_boolean_to_tokio_async_write(&mut buffer, false)
97    ///     .await?;
98    /// assert_eq!(buffer, br#"{"head":{},"boolean":false}"#);
99    /// # Ok(())
100    /// # }
101    /// ```
102    #[cfg(feature = "async-tokio")]
103    pub async fn serialize_boolean_to_tokio_async_write<W: AsyncWrite + Unpin>(
104        self,
105        writer: W,
106        value: bool,
107    ) -> io::Result<W> {
108        match self.format {
109            QueryResultsFormat::Xml => tokio_async_write_boolean_xml_result(writer, value).await,
110            QueryResultsFormat::Json => tokio_async_write_boolean_json_result(writer, value).await,
111            QueryResultsFormat::Csv | QueryResultsFormat::Tsv => {
112                tokio_async_write_boolean_csv_result(writer, value).await
113            }
114        }
115    }
116
117    #[deprecated(note = "use serialize_boolean_to_writer", since = "0.4.0")]
118    pub fn write_boolean_result<W: Write>(&self, writer: W, value: bool) -> io::Result<W> {
119        self.clone().serialize_boolean_to_writer(writer, value)
120    }
121
122    /// Returns a `SolutionsSerializer` allowing writing query solutions into the given [`Write`] implementation.
123    ///
124    /// <div class="warning">
125    ///
126    /// Do not forget to run the [`finish`](WriterSolutionsSerializer::finish()) method to properly write the last bytes of the file.</div>
127    ///
128    /// <div class="warning">
129    ///
130    /// This writer does unbuffered writes. You might want to use [`BufWriter`](io::BufWriter) to avoid that.</div>
131    ///
132    /// Example in XML (the API is the same for JSON, CSV and TSV):
133    /// ```
134    /// use sparesults::{QueryResultsFormat, QueryResultsSerializer};
135    /// use oxrdf::{LiteralRef, Variable, VariableRef};
136    /// use std::iter::once;
137    ///
138    /// let xml_serializer = QueryResultsSerializer::from_format(QueryResultsFormat::Xml);
139    /// let mut buffer = Vec::new();
140    /// let mut serializer = xml_serializer.serialize_solutions_to_writer(&mut buffer, vec![Variable::new("foo")?, Variable::new("bar")?])?;
141    /// serializer.serialize(once((VariableRef::new("foo")?, LiteralRef::from("test"))))?;
142    /// serializer.finish()?;
143    /// assert_eq!(buffer, br#"<?xml version="1.0"?><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>"#);
144    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
145    /// ```
146    pub fn serialize_solutions_to_writer<W: Write>(
147        self,
148        writer: W,
149        variables: Vec<Variable>,
150    ) -> io::Result<WriterSolutionsSerializer<W>> {
151        Ok(WriterSolutionsSerializer {
152            formatter: match self.format {
153                QueryResultsFormat::Xml => WriterSolutionsSerializerKind::Xml(
154                    WriterXmlSolutionsSerializer::start(writer, &variables)?,
155                ),
156                QueryResultsFormat::Json => WriterSolutionsSerializerKind::Json(
157                    WriterJsonSolutionsSerializer::start(writer, &variables)?,
158                ),
159                QueryResultsFormat::Csv => WriterSolutionsSerializerKind::Csv(
160                    WriterCsvSolutionsSerializer::start(writer, variables)?,
161                ),
162                QueryResultsFormat::Tsv => WriterSolutionsSerializerKind::Tsv(
163                    WriterTsvSolutionsSerializer::start(writer, variables)?,
164                ),
165            },
166        })
167    }
168
169    /// Returns a `SolutionsSerializer` allowing writing query solutions into the given [`Write`] implementation.
170    ///
171    /// <div class="warning">
172    ///
173    /// Do not forget to run the [`finish`](WriterSolutionsSerializer::finish()) method to properly write the last bytes of the file.</div>
174    ///
175    /// <div class="warning">
176    ///
177    /// This writer does unbuffered writes. You might want to use [`BufWriter`](io::BufWriter) to avoid that.</div>
178    ///
179    /// Example in XML (the API is the same for JSON, CSV and TSV):
180    /// ```
181    /// # #[tokio::main(flavor = "current_thread")]
182    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
183    /// use sparesults::{QueryResultsFormat, QueryResultsSerializer};
184    /// use oxrdf::{LiteralRef, Variable, VariableRef};
185    /// use std::iter::once;
186    ///
187    /// let json_serializer = QueryResultsSerializer::from_format(QueryResultsFormat::Json);
188    /// let mut buffer = Vec::new();
189    /// let mut serializer = json_serializer.serialize_solutions_to_tokio_async_write(&mut buffer, vec![Variable::new("foo")?, Variable::new("bar")?]).await?;
190    /// serializer.serialize(once((VariableRef::new("foo")?, LiteralRef::from("test")))).await?;
191    /// serializer.finish().await?;
192    /// assert_eq!(buffer, br#"{"head":{"vars":["foo","bar"]},"results":{"bindings":[{"foo":{"type":"literal","value":"test"}}]}}"#);
193    /// # Ok(())
194    /// # }    
195    /// ```
196    #[cfg(feature = "async-tokio")]
197    pub async fn serialize_solutions_to_tokio_async_write<W: AsyncWrite + Unpin>(
198        self,
199        writer: W,
200        variables: Vec<Variable>,
201    ) -> io::Result<TokioAsyncWriterSolutionsSerializer<W>> {
202        Ok(TokioAsyncWriterSolutionsSerializer {
203            formatter: match self.format {
204                QueryResultsFormat::Xml => TokioAsyncWriterSolutionsSerializerKind::Xml(
205                    TokioAsyncWriterXmlSolutionsSerializer::start(writer, &variables).await?,
206                ),
207                QueryResultsFormat::Json => TokioAsyncWriterSolutionsSerializerKind::Json(
208                    TokioAsyncWriterJsonSolutionsSerializer::start(writer, &variables).await?,
209                ),
210                QueryResultsFormat::Csv => TokioAsyncWriterSolutionsSerializerKind::Csv(
211                    TokioAsyncWriterCsvSolutionsSerializer::start(writer, variables).await?,
212                ),
213                QueryResultsFormat::Tsv => TokioAsyncWriterSolutionsSerializerKind::Tsv(
214                    TokioAsyncWriterTsvSolutionsSerializer::start(writer, variables).await?,
215                ),
216            },
217        })
218    }
219
220    #[deprecated(note = "use serialize_solutions_to_writer", since = "0.4.0")]
221    pub fn solutions_writer<W: Write>(
222        &self,
223        writer: W,
224        variables: Vec<Variable>,
225    ) -> io::Result<WriterSolutionsSerializer<W>> {
226        Self {
227            format: self.format,
228        }
229        .serialize_solutions_to_writer(writer, variables)
230    }
231}
232
233impl From<QueryResultsFormat> for QueryResultsSerializer {
234    fn from(format: QueryResultsFormat) -> Self {
235        Self::from_format(format)
236    }
237}
238
239/// Allows writing query results into a [`Write`] implementation.
240///
241/// Could be built using a [`QueryResultsSerializer`].
242///
243/// <div class="warning">
244///
245/// Do not forget to run the [`finish`](WriterSolutionsSerializer::finish()) method to properly write the last bytes of the file.</div>
246///
247/// <div class="warning">
248///
249/// This writer does unbuffered writes. You might want to use [`BufWriter`](io::BufWriter) to avoid that.</div>
250///
251/// Example in TSV (the API is the same for JSON, XML and CSV):
252/// ```
253/// use oxrdf::{LiteralRef, Variable, VariableRef};
254/// use sparesults::{QueryResultsFormat, QueryResultsSerializer};
255/// use std::iter::once;
256///
257/// let tsv_serializer = QueryResultsSerializer::from_format(QueryResultsFormat::Tsv);
258/// let mut buffer = Vec::new();
259/// let mut serializer = tsv_serializer.serialize_solutions_to_writer(
260///     &mut buffer,
261///     vec![Variable::new("foo")?, Variable::new("bar")?],
262/// )?;
263/// serializer.serialize(once((VariableRef::new("foo")?, LiteralRef::from("test"))))?;
264/// serializer.finish()?;
265/// assert_eq!(buffer, b"?foo\t?bar\n\"test\"\t\n");
266/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
267/// ```
268#[must_use]
269pub struct WriterSolutionsSerializer<W: Write> {
270    formatter: WriterSolutionsSerializerKind<W>,
271}
272
273enum WriterSolutionsSerializerKind<W: Write> {
274    Xml(WriterXmlSolutionsSerializer<W>),
275    Json(WriterJsonSolutionsSerializer<W>),
276    Csv(WriterCsvSolutionsSerializer<W>),
277    Tsv(WriterTsvSolutionsSerializer<W>),
278}
279
280impl<W: Write> WriterSolutionsSerializer<W> {
281    /// Writes a solution.
282    ///
283    /// Example in JSON (the API is the same for XML, CSV and TSV):
284    /// ```
285    /// use sparesults::{QueryResultsFormat, QueryResultsSerializer, QuerySolution};
286    /// use oxrdf::{Literal, LiteralRef, Variable, VariableRef};
287    /// use std::iter::once;
288    ///
289    /// let json_serializer = QueryResultsSerializer::from_format(QueryResultsFormat::Json);
290    /// let mut buffer = Vec::new();
291    /// let mut serializer = json_serializer.serialize_solutions_to_writer(&mut buffer, vec![Variable::new("foo")?, Variable::new("bar")?])?;
292    /// serializer.serialize(once((VariableRef::new("foo")?, LiteralRef::from("test"))))?;
293    /// serializer.serialize(&QuerySolution::from((vec![Variable::new("bar")?], vec![Some(Literal::from("test").into())])))?;
294    /// serializer.finish()?;
295    /// assert_eq!(buffer, br#"{"head":{"vars":["foo","bar"]},"results":{"bindings":[{"foo":{"type":"literal","value":"test"}},{"bar":{"type":"literal","value":"test"}}]}}"#);
296    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
297    /// ```
298    pub fn serialize<'a>(
299        &mut self,
300        solution: impl IntoIterator<Item = (impl Into<VariableRef<'a>>, impl Into<TermRef<'a>>)>,
301    ) -> io::Result<()> {
302        let solution = solution.into_iter().map(|(v, s)| (v.into(), s.into()));
303        match &mut self.formatter {
304            WriterSolutionsSerializerKind::Xml(writer) => writer.serialize(solution),
305            WriterSolutionsSerializerKind::Json(writer) => writer.serialize(solution),
306            WriterSolutionsSerializerKind::Csv(writer) => writer.serialize(solution),
307            WriterSolutionsSerializerKind::Tsv(writer) => writer.serialize(solution),
308        }
309    }
310
311    /// Writes the last bytes of the file.
312    pub fn finish(self) -> io::Result<W> {
313        match self.formatter {
314            WriterSolutionsSerializerKind::Xml(serializer) => serializer.finish(),
315            WriterSolutionsSerializerKind::Json(serializer) => serializer.finish(),
316            WriterSolutionsSerializerKind::Csv(serializer) => Ok(serializer.finish()),
317            WriterSolutionsSerializerKind::Tsv(serializer) => Ok(serializer.finish()),
318        }
319    }
320}
321
322/// Allows writing query results into an [`AsyncWrite`] implementation.
323///
324/// Could be built using a [`QueryResultsSerializer`].
325///
326/// <div class="warning">
327///
328/// Do not forget to run the [`finish`](TokioAsyncWriterSolutionsSerializer::finish()) method to properly write the last bytes of the file.</div>
329///
330/// <div class="warning">
331///
332/// This writer does unbuffered writes. You might want to use [`BufWriter`](tokio::io::BufWriter) to avoid that.</div>
333///
334/// Example in TSV (the API is the same for JSON, CSV and XML):
335/// ```
336/// # #[tokio::main(flavor = "current_thread")]
337/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
338/// use oxrdf::{LiteralRef, Variable, VariableRef};
339/// use sparesults::{QueryResultsFormat, QueryResultsSerializer};
340/// use std::iter::once;
341///
342/// let tsv_serializer = QueryResultsSerializer::from_format(QueryResultsFormat::Tsv);
343/// let mut buffer = Vec::new();
344/// let mut serializer = tsv_serializer
345///     .serialize_solutions_to_tokio_async_write(
346///         &mut buffer,
347///         vec![Variable::new("foo")?, Variable::new("bar")?],
348///     )
349///     .await?;
350/// serializer
351///     .serialize(once((VariableRef::new("foo")?, LiteralRef::from("test"))))
352///     .await?;
353/// serializer.finish().await?;
354/// assert_eq!(buffer, b"?foo\t?bar\n\"test\"\t\n");
355/// # Ok(())
356/// # }
357/// ```
358#[cfg(feature = "async-tokio")]
359#[must_use]
360pub struct TokioAsyncWriterSolutionsSerializer<W: AsyncWrite + Unpin> {
361    formatter: TokioAsyncWriterSolutionsSerializerKind<W>,
362}
363
364#[cfg(feature = "async-tokio")]
365enum TokioAsyncWriterSolutionsSerializerKind<W: AsyncWrite + Unpin> {
366    Xml(TokioAsyncWriterXmlSolutionsSerializer<W>),
367    Json(TokioAsyncWriterJsonSolutionsSerializer<W>),
368    Csv(TokioAsyncWriterCsvSolutionsSerializer<W>),
369    Tsv(TokioAsyncWriterTsvSolutionsSerializer<W>),
370}
371
372#[cfg(feature = "async-tokio")]
373impl<W: AsyncWrite + Unpin> TokioAsyncWriterSolutionsSerializer<W> {
374    /// Writes a solution.
375    ///
376    /// Example in JSON (the API is the same for XML, CSV and TSV):
377    /// ```
378    /// # #[tokio::main(flavor = "current_thread")]
379    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
380    /// use sparesults::{QueryResultsFormat, QueryResultsSerializer, QuerySolution};
381    /// use oxrdf::{Literal, LiteralRef, Variable, VariableRef};
382    /// use std::iter::once;
383    ///
384    /// let json_serializer = QueryResultsSerializer::from_format(QueryResultsFormat::Json);
385    /// let mut buffer = Vec::new();
386    /// let mut serializer = json_serializer.serialize_solutions_to_tokio_async_write(&mut buffer, vec![Variable::new("foo")?, Variable::new("bar")?]).await?;
387    /// serializer.serialize(once((VariableRef::new("foo")?, LiteralRef::from("test")))).await?;
388    /// serializer.serialize(&QuerySolution::from((vec![Variable::new("bar")?], vec![Some(Literal::from("test").into())]))).await?;
389    /// serializer.finish().await?;
390    /// assert_eq!(buffer, br#"{"head":{"vars":["foo","bar"]},"results":{"bindings":[{"foo":{"type":"literal","value":"test"}},{"bar":{"type":"literal","value":"test"}}]}}"#);
391    /// # Ok(())
392    /// # }
393    /// ```
394    pub async fn serialize<'a>(
395        &mut self,
396        solution: impl IntoIterator<Item = (impl Into<VariableRef<'a>>, impl Into<TermRef<'a>>)>,
397    ) -> io::Result<()> {
398        let solution = solution.into_iter().map(|(v, s)| (v.into(), s.into()));
399        match &mut self.formatter {
400            TokioAsyncWriterSolutionsSerializerKind::Xml(writer) => {
401                writer.serialize(solution).await
402            }
403            TokioAsyncWriterSolutionsSerializerKind::Json(writer) => {
404                writer.serialize(solution).await
405            }
406            TokioAsyncWriterSolutionsSerializerKind::Csv(writer) => {
407                writer.serialize(solution).await
408            }
409            TokioAsyncWriterSolutionsSerializerKind::Tsv(writer) => {
410                writer.serialize(solution).await
411            }
412        }
413    }
414
415    /// Writes the last bytes of the file.
416    pub async fn finish(self) -> io::Result<W> {
417        match self.formatter {
418            TokioAsyncWriterSolutionsSerializerKind::Xml(serializer) => serializer.finish().await,
419            TokioAsyncWriterSolutionsSerializerKind::Json(serializer) => serializer.finish().await,
420            TokioAsyncWriterSolutionsSerializerKind::Csv(serializer) => Ok(serializer.finish()),
421            TokioAsyncWriterSolutionsSerializerKind::Tsv(serializer) => Ok(serializer.finish()),
422        }
423    }
424}