Skip to main content

sparesults/
serializer.rs

1#[cfg(feature = "async-tokio")]
2use crate::csv::{
3    TokioAsyncWriterCsvSolutionsSerializer, TokioAsyncWriterTsvSolutionsSerializer,
4    tokio_async_write_boolean_csv_result,
5};
6use crate::csv::{
7    WriterCsvSolutionsSerializer, WriterTsvSolutionsSerializer, write_boolean_csv_result,
8};
9use crate::format::QueryResultsFormat;
10#[cfg(feature = "async-tokio")]
11use crate::json::{TokioAsyncWriterJsonSolutionsSerializer, tokio_async_write_boolean_json_result};
12use crate::json::{WriterJsonSolutionsSerializer, write_boolean_json_result};
13#[cfg(feature = "async-tokio")]
14use crate::xml::{TokioAsyncWriterXmlSolutionsSerializer, tokio_async_write_boolean_xml_result};
15use crate::xml::{WriterXmlSolutionsSerializer, write_boolean_xml_result};
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    /// Returns a `SolutionsSerializer` allowing writing query solutions into the given [`Write`] implementation.
118    ///
119    /// <div class="warning">
120    ///
121    /// Do not forget to run the [`finish`](WriterSolutionsSerializer::finish()) method to properly write the last bytes of the file.</div>
122    ///
123    /// <div class="warning">
124    ///
125    /// This writer does unbuffered writes. You might want to use [`BufWriter`](io::BufWriter) to avoid that.</div>
126    ///
127    /// Example in XML (the API is the same for JSON, CSV and TSV):
128    /// ```
129    /// use sparesults::{QueryResultsFormat, QueryResultsSerializer};
130    /// use oxrdf::{LiteralRef, Variable, VariableRef};
131    /// use std::iter::once;
132    ///
133    /// let xml_serializer = QueryResultsSerializer::from_format(QueryResultsFormat::Xml);
134    /// let mut buffer = Vec::new();
135    /// let mut serializer = xml_serializer.serialize_solutions_to_writer(&mut buffer, vec![Variable::new("foo")?, Variable::new("bar")?])?;
136    /// serializer.serialize(once((VariableRef::new("foo")?, LiteralRef::from("test"))))?;
137    /// serializer.finish()?;
138    /// 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>"#);
139    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
140    /// ```
141    pub fn serialize_solutions_to_writer<W: Write>(
142        self,
143        writer: W,
144        variables: Vec<Variable>,
145    ) -> io::Result<WriterSolutionsSerializer<W>> {
146        Ok(WriterSolutionsSerializer {
147            formatter: match self.format {
148                QueryResultsFormat::Xml => WriterSolutionsSerializerKind::Xml(
149                    WriterXmlSolutionsSerializer::start(writer, &variables)?,
150                ),
151                QueryResultsFormat::Json => WriterSolutionsSerializerKind::Json(
152                    WriterJsonSolutionsSerializer::start(writer, &variables)?,
153                ),
154                QueryResultsFormat::Csv => WriterSolutionsSerializerKind::Csv(
155                    WriterCsvSolutionsSerializer::start(writer, variables)?,
156                ),
157                QueryResultsFormat::Tsv => WriterSolutionsSerializerKind::Tsv(
158                    WriterTsvSolutionsSerializer::start(writer, variables)?,
159                ),
160            },
161        })
162    }
163
164    /// Returns a `SolutionsSerializer` allowing writing query solutions into the given [`Write`] implementation.
165    ///
166    /// <div class="warning">
167    ///
168    /// Do not forget to run the [`finish`](WriterSolutionsSerializer::finish()) method to properly write the last bytes of the file.</div>
169    ///
170    /// <div class="warning">
171    ///
172    /// This writer does unbuffered writes. You might want to use [`BufWriter`](io::BufWriter) to avoid that.</div>
173    ///
174    /// Example in XML (the API is the same for JSON, CSV and TSV):
175    /// ```
176    /// # #[tokio::main(flavor = "current_thread")]
177    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
178    /// use sparesults::{QueryResultsFormat, QueryResultsSerializer};
179    /// use oxrdf::{LiteralRef, Variable, VariableRef};
180    /// use std::iter::once;
181    ///
182    /// let json_serializer = QueryResultsSerializer::from_format(QueryResultsFormat::Json);
183    /// let mut buffer = Vec::new();
184    /// let mut serializer = json_serializer.serialize_solutions_to_tokio_async_write(&mut buffer, vec![Variable::new("foo")?, Variable::new("bar")?]).await?;
185    /// serializer.serialize(once((VariableRef::new("foo")?, LiteralRef::from("test")))).await?;
186    /// serializer.finish().await?;
187    /// assert_eq!(buffer, br#"{"head":{"vars":["foo","bar"]},"results":{"bindings":[{"foo":{"type":"literal","value":"test"}}]}}"#);
188    /// # Ok(())
189    /// # }    
190    /// ```
191    #[cfg(feature = "async-tokio")]
192    pub async fn serialize_solutions_to_tokio_async_write<W: AsyncWrite + Unpin>(
193        self,
194        writer: W,
195        variables: Vec<Variable>,
196    ) -> io::Result<TokioAsyncWriterSolutionsSerializer<W>> {
197        Ok(TokioAsyncWriterSolutionsSerializer {
198            formatter: match self.format {
199                QueryResultsFormat::Xml => TokioAsyncWriterSolutionsSerializerKind::Xml(
200                    TokioAsyncWriterXmlSolutionsSerializer::start(writer, &variables).await?,
201                ),
202                QueryResultsFormat::Json => TokioAsyncWriterSolutionsSerializerKind::Json(
203                    TokioAsyncWriterJsonSolutionsSerializer::start(writer, &variables).await?,
204                ),
205                QueryResultsFormat::Csv => TokioAsyncWriterSolutionsSerializerKind::Csv(
206                    TokioAsyncWriterCsvSolutionsSerializer::start(writer, variables).await?,
207                ),
208                QueryResultsFormat::Tsv => TokioAsyncWriterSolutionsSerializerKind::Tsv(
209                    TokioAsyncWriterTsvSolutionsSerializer::start(writer, variables).await?,
210                ),
211            },
212        })
213    }
214}
215
216impl From<QueryResultsFormat> for QueryResultsSerializer {
217    fn from(format: QueryResultsFormat) -> Self {
218        Self::from_format(format)
219    }
220}
221
222/// Allows writing query results into a [`Write`] implementation.
223///
224/// Could be built using a [`QueryResultsSerializer`].
225///
226/// <div class="warning">
227///
228/// Do not forget to run the [`finish`](WriterSolutionsSerializer::finish()) method to properly write the last bytes of the file.</div>
229///
230/// <div class="warning">
231///
232/// This writer does unbuffered writes. You might want to use [`BufWriter`](io::BufWriter) to avoid that.</div>
233///
234/// Example in TSV (the API is the same for JSON, XML and CSV):
235/// ```
236/// use oxrdf::{LiteralRef, Variable, VariableRef};
237/// use sparesults::{QueryResultsFormat, QueryResultsSerializer};
238/// use std::iter::once;
239///
240/// let tsv_serializer = QueryResultsSerializer::from_format(QueryResultsFormat::Tsv);
241/// let mut buffer = Vec::new();
242/// let mut serializer = tsv_serializer.serialize_solutions_to_writer(
243///     &mut buffer,
244///     vec![Variable::new("foo")?, Variable::new("bar")?],
245/// )?;
246/// serializer.serialize(once((VariableRef::new("foo")?, LiteralRef::from("test"))))?;
247/// serializer.finish()?;
248/// assert_eq!(buffer, b"?foo\t?bar\n\"test\"\t\n");
249/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
250/// ```
251#[must_use]
252pub struct WriterSolutionsSerializer<W: Write> {
253    formatter: WriterSolutionsSerializerKind<W>,
254}
255
256enum WriterSolutionsSerializerKind<W: Write> {
257    Xml(WriterXmlSolutionsSerializer<W>),
258    Json(WriterJsonSolutionsSerializer<W>),
259    Csv(WriterCsvSolutionsSerializer<W>),
260    Tsv(WriterTsvSolutionsSerializer<W>),
261}
262
263impl<W: Write> WriterSolutionsSerializer<W> {
264    /// Writes a solution.
265    ///
266    /// Example in JSON (the API is the same for XML, CSV and TSV):
267    /// ```
268    /// use sparesults::{QueryResultsFormat, QueryResultsSerializer, QuerySolution};
269    /// use oxrdf::{Literal, LiteralRef, Variable, VariableRef};
270    /// use std::iter::once;
271    ///
272    /// let json_serializer = QueryResultsSerializer::from_format(QueryResultsFormat::Json);
273    /// let mut buffer = Vec::new();
274    /// let mut serializer = json_serializer.serialize_solutions_to_writer(&mut buffer, vec![Variable::new("foo")?, Variable::new("bar")?])?;
275    /// serializer.serialize(once((VariableRef::new("foo")?, LiteralRef::from("test"))))?;
276    /// serializer.serialize(&QuerySolution::from((vec![Variable::new("bar")?], vec![Some(Literal::from("test").into())])))?;
277    /// serializer.finish()?;
278    /// assert_eq!(buffer, br#"{"head":{"vars":["foo","bar"]},"results":{"bindings":[{"foo":{"type":"literal","value":"test"}},{"bar":{"type":"literal","value":"test"}}]}}"#);
279    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
280    /// ```
281    pub fn serialize<'a>(
282        &mut self,
283        solution: impl IntoIterator<Item = (impl Into<VariableRef<'a>>, impl Into<TermRef<'a>>)>,
284    ) -> io::Result<()> {
285        let solution = solution.into_iter().map(|(v, s)| (v.into(), s.into()));
286        match &mut self.formatter {
287            WriterSolutionsSerializerKind::Xml(writer) => writer.serialize(solution),
288            WriterSolutionsSerializerKind::Json(writer) => writer.serialize(solution),
289            WriterSolutionsSerializerKind::Csv(writer) => writer.serialize(solution),
290            WriterSolutionsSerializerKind::Tsv(writer) => writer.serialize(solution),
291        }
292    }
293
294    /// Writes the last bytes of the file.
295    pub fn finish(self) -> io::Result<W> {
296        match self.formatter {
297            WriterSolutionsSerializerKind::Xml(serializer) => serializer.finish(),
298            WriterSolutionsSerializerKind::Json(serializer) => serializer.finish(),
299            WriterSolutionsSerializerKind::Csv(serializer) => Ok(serializer.finish()),
300            WriterSolutionsSerializerKind::Tsv(serializer) => Ok(serializer.finish()),
301        }
302    }
303}
304
305/// Allows writing query results into an [`AsyncWrite`] implementation.
306///
307/// Could be built using a [`QueryResultsSerializer`].
308///
309/// <div class="warning">
310///
311/// Do not forget to run the [`finish`](TokioAsyncWriterSolutionsSerializer::finish()) method to properly write the last bytes of the file.</div>
312///
313/// <div class="warning">
314///
315/// This writer does unbuffered writes. You might want to use [`BufWriter`](tokio::io::BufWriter) to avoid that.</div>
316///
317/// Example in TSV (the API is the same for JSON, CSV and XML):
318/// ```
319/// # #[tokio::main(flavor = "current_thread")]
320/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
321/// use oxrdf::{LiteralRef, Variable, VariableRef};
322/// use sparesults::{QueryResultsFormat, QueryResultsSerializer};
323/// use std::iter::once;
324///
325/// let tsv_serializer = QueryResultsSerializer::from_format(QueryResultsFormat::Tsv);
326/// let mut buffer = Vec::new();
327/// let mut serializer = tsv_serializer
328///     .serialize_solutions_to_tokio_async_write(
329///         &mut buffer,
330///         vec![Variable::new("foo")?, Variable::new("bar")?],
331///     )
332///     .await?;
333/// serializer
334///     .serialize(once((VariableRef::new("foo")?, LiteralRef::from("test"))))
335///     .await?;
336/// serializer.finish().await?;
337/// assert_eq!(buffer, b"?foo\t?bar\n\"test\"\t\n");
338/// # Ok(())
339/// # }
340/// ```
341#[cfg(feature = "async-tokio")]
342#[must_use]
343pub struct TokioAsyncWriterSolutionsSerializer<W: AsyncWrite + Unpin> {
344    formatter: TokioAsyncWriterSolutionsSerializerKind<W>,
345}
346
347#[cfg(feature = "async-tokio")]
348enum TokioAsyncWriterSolutionsSerializerKind<W: AsyncWrite + Unpin> {
349    Xml(TokioAsyncWriterXmlSolutionsSerializer<W>),
350    Json(TokioAsyncWriterJsonSolutionsSerializer<W>),
351    Csv(TokioAsyncWriterCsvSolutionsSerializer<W>),
352    Tsv(TokioAsyncWriterTsvSolutionsSerializer<W>),
353}
354
355#[cfg(feature = "async-tokio")]
356impl<W: AsyncWrite + Unpin> TokioAsyncWriterSolutionsSerializer<W> {
357    /// Writes a solution.
358    ///
359    /// Example in JSON (the API is the same for XML, CSV and TSV):
360    /// ```
361    /// # #[tokio::main(flavor = "current_thread")]
362    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
363    /// use sparesults::{QueryResultsFormat, QueryResultsSerializer, QuerySolution};
364    /// use oxrdf::{Literal, LiteralRef, Variable, VariableRef};
365    /// use std::iter::once;
366    ///
367    /// let json_serializer = QueryResultsSerializer::from_format(QueryResultsFormat::Json);
368    /// let mut buffer = Vec::new();
369    /// let mut serializer = json_serializer.serialize_solutions_to_tokio_async_write(&mut buffer, vec![Variable::new("foo")?, Variable::new("bar")?]).await?;
370    /// serializer.serialize(once((VariableRef::new("foo")?, LiteralRef::from("test")))).await?;
371    /// serializer.serialize(&QuerySolution::from((vec![Variable::new("bar")?], vec![Some(Literal::from("test").into())]))).await?;
372    /// serializer.finish().await?;
373    /// assert_eq!(buffer, br#"{"head":{"vars":["foo","bar"]},"results":{"bindings":[{"foo":{"type":"literal","value":"test"}},{"bar":{"type":"literal","value":"test"}}]}}"#);
374    /// # Ok(())
375    /// # }
376    /// ```
377    pub async fn serialize<'a>(
378        &mut self,
379        solution: impl IntoIterator<Item = (impl Into<VariableRef<'a>>, impl Into<TermRef<'a>>)>,
380    ) -> io::Result<()> {
381        let solution = solution.into_iter().map(|(v, s)| (v.into(), s.into()));
382        match &mut self.formatter {
383            TokioAsyncWriterSolutionsSerializerKind::Xml(writer) => {
384                writer.serialize(solution).await
385            }
386            TokioAsyncWriterSolutionsSerializerKind::Json(writer) => {
387                writer.serialize(solution).await
388            }
389            TokioAsyncWriterSolutionsSerializerKind::Csv(writer) => {
390                writer.serialize(solution).await
391            }
392            TokioAsyncWriterSolutionsSerializerKind::Tsv(writer) => {
393                writer.serialize(solution).await
394            }
395        }
396    }
397
398    /// Writes the last bytes of the file.
399    pub async fn finish(self) -> io::Result<W> {
400        match self.formatter {
401            TokioAsyncWriterSolutionsSerializerKind::Xml(serializer) => serializer.finish().await,
402            TokioAsyncWriterSolutionsSerializerKind::Json(serializer) => serializer.finish().await,
403            TokioAsyncWriterSolutionsSerializerKind::Csv(serializer) => Ok(serializer.finish()),
404            TokioAsyncWriterSolutionsSerializerKind::Tsv(serializer) => Ok(serializer.finish()),
405        }
406    }
407}