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}