sophia_turtle/serializer/
nq.rs

1//! Serializer for the [N-Quads] concrete syntax of RDF.
2//!
3//! **Important**:
4//! the methods in this module accepting a [`Write`]
5//! make no effort to minimize the number of write operations.
6//! Hence, in most cased, they should be passed a [`BufWriter`].
7//!
8//! [N-Quads]: https://www.w3.org/TR/n-quads/
9//! [`Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
10//! [`BufWriter`]: https://doc.rust-lang.org/std/io/struct.BufWriter.html
11
12use super::nt::{write_term, write_triple};
13use sophia_api::quad::Quad;
14use sophia_api::serializer::*;
15use sophia_api::source::{QuadSource, StreamResult};
16use std::io;
17
18/// N-Quads serializer configuration.
19pub type NqConfig = super::nt::NtConfig;
20
21/// N-Quads serializer.
22pub struct NqSerializer<W> {
23    config: NqConfig,
24    write: W,
25}
26
27impl<W> NqSerializer<W>
28where
29    W: io::Write,
30{
31    /// Build a new N-Quads serializer writing to `write`, with the default config.
32    #[inline]
33    pub fn new(write: W) -> NqSerializer<W> {
34        Self::new_with_config(write, NqConfig::default())
35    }
36
37    /// Build a new N-Quads serializer writing to `write`, with the given config.
38    pub fn new_with_config(write: W, config: NqConfig) -> NqSerializer<W> {
39        NqSerializer { config, write }
40    }
41
42    /// Borrow this serializer's configuration.
43    pub fn config(&self) -> &NqConfig {
44        &self.config
45    }
46}
47
48impl<W> QuadSerializer for NqSerializer<W>
49where
50    W: io::Write,
51{
52    type Error = io::Error;
53
54    fn serialize_quads<QS>(
55        &mut self,
56        mut source: QS,
57    ) -> StreamResult<&mut Self, QS::Error, Self::Error>
58    where
59        QS: QuadSource,
60    {
61        if self.config.ascii {
62            todo!("Pure-ASCII N-Quads is not implemented yet")
63        }
64        source
65            .try_for_each_quad(|q| {
66                {
67                    let w = &mut self.write;
68                    let (tr, gn) = q.spog();
69                    write_triple(w, tr)?;
70                    match gn {
71                        None => w.write_all(b".\n"),
72                        Some(t) => {
73                            w.write_all(b" ")?;
74                            write_term(w, t)?;
75                            w.write_all(b".\n")
76                        }
77                    }
78                }
79                .map_err(|e| io::Error::new(io::ErrorKind::Other, e))
80            })
81            .map(|_| self)
82    }
83}
84
85impl NqSerializer<Vec<u8>> {
86    /// Create a new serializer which targets a `String`.
87    #[inline]
88    pub fn new_stringifier() -> Self {
89        NqSerializer::new(Vec::new())
90    }
91    /// Create a new serializer which targets a `String` with a custom config.
92    #[inline]
93    pub fn new_stringifier_with_config(config: NqConfig) -> Self {
94        NqSerializer::new_with_config(Vec::new(), config)
95    }
96}
97
98impl Stringifier for NqSerializer<Vec<u8>> {
99    fn as_utf8(&self) -> &[u8] {
100        &self.write[..]
101    }
102}
103
104// ---------------------------------------------------------------------------------
105//                                      tests
106// ---------------------------------------------------------------------------------
107
108#[cfg(test)]
109pub(crate) mod test {
110    use super::*;
111    use sophia_api::dataset::MutableDataset;
112    use sophia_api::ns::*;
113    use sophia_api::quad::Spog;
114    use sophia_api::term::{BnodeId, LanguageTag, SimpleTerm, VarName};
115    use sophia_iri::Iri;
116
117    #[test]
118    fn graph() -> Result<(), Box<dyn std::error::Error>> {
119        let me = BnodeId::new_unchecked("me");
120        let mut d: Vec<Spog<SimpleTerm<'static>>> = vec![];
121        MutableDataset::insert(
122            &mut d,
123            me,
124            rdf::type_,
125            Iri::new_unchecked("http://schema.org/Person"),
126            None as Option<i32>,
127        )?;
128        MutableDataset::insert(
129            &mut d,
130            me,
131            Iri::new_unchecked("http://schema.org/name"),
132            "Pierre-Antoine",
133            Some(me),
134        )?;
135        MutableDataset::insert(
136            &mut d,
137            me,
138            Iri::new_unchecked("http://example.org/value"),
139            42,
140            Some(me),
141        )?;
142        MutableDataset::insert(
143            &mut d,
144            me,
145            Iri::new_unchecked("http://example.org/message"),
146            SimpleTerm::LiteralLanguage(
147                "hello\nworld".into(),
148                LanguageTag::new_unchecked("en".into()),
149            ),
150            Some(Iri::new_unchecked("tag:g1")),
151        )?;
152        let tr = d[0].spog().0.map(Clone::clone);
153        MutableDataset::insert(
154            &mut d,
155            SimpleTerm::Triple(Box::new(tr)),
156            Iri::new_unchecked("http://schema.org/creator"),
157            VarName::new_unchecked("x"),
158            None as Option<i32>,
159        )?;
160
161        let s = NqSerializer::new_stringifier()
162            .serialize_dataset(&d)
163            .unwrap()
164            .to_string();
165        assert_eq!(
166            &s,
167            r#"_:me <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://schema.org/Person>.
168_:me <http://schema.org/name> "Pierre-Antoine" _:me.
169_:me <http://example.org/value> "42"^^<http://www.w3.org/2001/XMLSchema#integer> _:me.
170_:me <http://example.org/message> "hello\nworld"@en <tag:g1>.
171<<_:me <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://schema.org/Person>>> <http://schema.org/creator> ?x.
172"#
173        );
174        Ok(())
175    }
176}