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}