1use super::error::FormatError;
9use crate::model::{
10 GraphName, Literal, NamedNode, ObjectRef, PredicateRef, Quad, QuadRef, SubjectRef,
11};
12use std::collections::{BTreeMap, HashMap};
13use std::io::Write;
14
15#[derive(Debug, Clone)]
17pub struct TriGSerializer {
18 base_iri: Option<String>,
20 prefixes: HashMap<String, String>,
22 pretty: bool,
24}
25
26impl TriGSerializer {
27 pub fn new() -> Self {
29 let mut prefixes = HashMap::new();
30
31 prefixes.insert(
33 "rdf".to_string(),
34 "http://www.w3.org/1999/02/22-rdf-syntax-ns#".to_string(),
35 );
36 prefixes.insert(
37 "rdfs".to_string(),
38 "http://www.w3.org/2000/01/rdf-schema#".to_string(),
39 );
40 prefixes.insert(
41 "xsd".to_string(),
42 "http://www.w3.org/2001/XMLSchema#".to_string(),
43 );
44
45 Self {
46 base_iri: None,
47 prefixes,
48 pretty: false,
49 }
50 }
51
52 pub fn with_base_iri(mut self, base: &str) -> Self {
54 self.base_iri = Some(base.to_string());
55 self
56 }
57
58 pub fn with_prefix(mut self, prefix: &str, iri: &str) -> Self {
60 self.prefixes.insert(prefix.to_string(), iri.to_string());
61 self
62 }
63
64 pub fn pretty(mut self) -> Self {
66 self.pretty = true;
67 self
68 }
69
70 pub fn for_writer<W: Write + 'static>(self, writer: W) -> TriGWriter<W> {
72 TriGWriter {
73 writer,
74 serializer: self,
75 buffer: Vec::new(),
76 }
77 }
78
79 fn serialize_quads<W: Write>(&self, quads: &[Quad], writer: &mut W) -> Result<(), FormatError> {
81 for (prefix, namespace) in &self.prefixes {
83 writeln!(writer, "@prefix {}: <{}> .", prefix, namespace).map_err(FormatError::from)?;
84 }
85
86 if !self.prefixes.is_empty() {
87 writeln!(writer).map_err(FormatError::from)?;
88 }
89
90 let grouped = self.group_quads_by_graph(quads);
92
93 for (graph_name, graph_quads) in grouped {
94 match graph_name {
95 GraphName::DefaultGraph => {
96 for quad in graph_quads {
98 self.serialize_triple(quad.as_ref(), writer)?;
99 writeln!(writer, " .").map_err(FormatError::from)?;
100 }
101 }
102 GraphName::NamedNode(node) => {
103 self.write_named_node(&node, writer)?;
105 writeln!(writer, " {{").map_err(FormatError::from)?;
106
107 for quad in graph_quads {
108 if self.pretty {
109 write!(writer, " ").map_err(FormatError::from)?;
110 }
111 self.serialize_triple(quad.as_ref(), writer)?;
112 writeln!(writer, " .").map_err(FormatError::from)?;
113 }
114
115 writeln!(writer, "}}").map_err(FormatError::from)?;
116 }
117 GraphName::BlankNode(node) => {
118 let id = node.as_str();
120 let id = id.strip_prefix("_:").unwrap_or(id);
121 writeln!(writer, "_:{} {{", id).map_err(FormatError::from)?;
122
123 for quad in graph_quads {
124 if self.pretty {
125 write!(writer, " ").map_err(FormatError::from)?;
126 }
127 self.serialize_triple(quad.as_ref(), writer)?;
128 writeln!(writer, " .").map_err(FormatError::from)?;
129 }
130
131 writeln!(writer, "}}").map_err(FormatError::from)?;
132 }
133 GraphName::Variable(_) => {
134 return Err(FormatError::InvalidData(
135 "Cannot serialize variable graph names".to_string(),
136 ));
137 }
138 }
139
140 if self.pretty {
141 writeln!(writer).map_err(FormatError::from)?;
142 }
143 }
144
145 Ok(())
146 }
147
148 fn serialize_triple<W: Write>(
149 &self,
150 quad: QuadRef<'_>,
151 writer: &mut W,
152 ) -> Result<(), FormatError> {
153 self.write_subject(quad.subject(), writer)?;
154 write!(writer, " ").map_err(FormatError::from)?;
155
156 self.write_predicate(quad.predicate(), writer)?;
157 write!(writer, " ").map_err(FormatError::from)?;
158
159 self.write_object(quad.object(), writer)?;
160
161 Ok(())
162 }
163
164 fn write_subject<W: Write>(
165 &self,
166 subject: SubjectRef<'_>,
167 writer: &mut W,
168 ) -> Result<(), FormatError> {
169 match subject {
170 SubjectRef::NamedNode(node) => self.write_named_node(node, writer)?,
171 SubjectRef::BlankNode(node) => {
172 let id = node.as_str();
173 let id = id.strip_prefix("_:").unwrap_or(id);
174 write!(writer, "_:{}", id).map_err(FormatError::from)?;
175 }
176 SubjectRef::Variable(var) => {
177 write!(writer, "?{}", var.name()).map_err(FormatError::from)?;
178 }
179 SubjectRef::QuotedTriple(qt) => self.write_quoted_triple(qt.inner(), writer)?,
180 }
181 Ok(())
182 }
183
184 fn write_quoted_triple<W: Write>(
185 &self,
186 inner: &crate::model::Triple,
187 writer: &mut W,
188 ) -> Result<(), FormatError> {
189 write!(writer, "<< ").map_err(FormatError::from)?;
190 self.write_subject(inner.subject().into(), writer)?;
191 write!(writer, " ").map_err(FormatError::from)?;
192 self.write_predicate(inner.predicate().into(), writer)?;
193 write!(writer, " ").map_err(FormatError::from)?;
194 self.write_object(inner.object().into(), writer)?;
195 write!(writer, " >>").map_err(FormatError::from)?;
196 Ok(())
197 }
198
199 fn write_predicate<W: Write>(
200 &self,
201 predicate: PredicateRef<'_>,
202 writer: &mut W,
203 ) -> Result<(), FormatError> {
204 match predicate {
205 PredicateRef::NamedNode(node) => {
206 if node.as_str() == "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" {
208 write!(writer, "a").map_err(FormatError::from)?;
209 } else {
210 self.write_named_node(node, writer)?;
211 }
212 }
213 PredicateRef::Variable(var) => {
214 write!(writer, "?{}", var.name()).map_err(FormatError::from)?;
215 }
216 }
217 Ok(())
218 }
219
220 fn write_object<W: Write>(
221 &self,
222 object: ObjectRef<'_>,
223 writer: &mut W,
224 ) -> Result<(), FormatError> {
225 match object {
226 ObjectRef::NamedNode(node) => self.write_named_node(node, writer)?,
227 ObjectRef::BlankNode(node) => {
228 let id = node.as_str();
229 let id = id.strip_prefix("_:").unwrap_or(id);
230 write!(writer, "_:{}", id).map_err(FormatError::from)?;
231 }
232 ObjectRef::Literal(literal) => self.write_literal(literal, writer)?,
233 ObjectRef::Variable(var) => {
234 write!(writer, "?{}", var.name()).map_err(FormatError::from)?;
235 }
236 ObjectRef::QuotedTriple(qt) => self.write_quoted_triple(qt.inner(), writer)?,
237 }
238 Ok(())
239 }
240
241 fn write_named_node<W: Write>(
242 &self,
243 node: &NamedNode,
244 writer: &mut W,
245 ) -> Result<(), FormatError> {
246 let iri = node.as_str();
247
248 for (prefix, namespace) in &self.prefixes {
250 if let Some(local) = iri.strip_prefix(namespace) {
251 write!(writer, "{}:{}", prefix, local).map_err(FormatError::from)?;
252 return Ok(());
253 }
254 }
255
256 write!(writer, "<{}>", iri).map_err(FormatError::from)?;
258 Ok(())
259 }
260
261 fn write_literal<W: Write>(
262 &self,
263 literal: &Literal,
264 writer: &mut W,
265 ) -> Result<(), FormatError> {
266 let value = literal.value();
267 let escaped = self.escape_string(value);
268
269 write!(writer, "\"{}\"", escaped).map_err(FormatError::from)?;
270
271 if let Some(lang) = literal.language() {
272 write!(writer, "@{}", lang).map_err(FormatError::from)?;
273 } else {
274 let datatype = literal.datatype();
275 if datatype.as_str() != "http://www.w3.org/2001/XMLSchema#string" {
276 write!(writer, "^^").map_err(FormatError::from)?;
277 self.write_named_node(&datatype.into_owned(), writer)?;
278 }
279 }
280
281 Ok(())
282 }
283
284 fn escape_string(&self, s: &str) -> String {
285 let mut result = String::with_capacity(s.len());
286 for ch in s.chars() {
287 match ch {
288 '\\' => result.push_str("\\\\"),
289 '\"' => result.push_str("\\\""),
290 '\n' => result.push_str("\\n"),
291 '\r' => result.push_str("\\r"),
292 '\t' => result.push_str("\\t"),
293 c if c.is_control() => {
294 result.push_str(&format!("\\u{:04X}", c as u32));
295 }
296 c => result.push(c),
297 }
298 }
299 result
300 }
301
302 fn group_quads_by_graph<'a>(&self, quads: &'a [Quad]) -> BTreeMap<GraphName, Vec<&'a Quad>> {
303 let mut grouped = BTreeMap::new();
304
305 for quad in quads {
306 grouped
307 .entry(quad.graph_name().clone())
308 .or_insert_with(Vec::new)
309 .push(quad);
310 }
311
312 grouped
313 }
314}
315
316impl Default for TriGSerializer {
317 fn default() -> Self {
318 Self::new()
319 }
320}
321
322pub struct TriGWriter<W: Write> {
324 writer: W,
325 serializer: TriGSerializer,
326 buffer: Vec<Quad>,
327}
328
329impl<W: Write> TriGWriter<W> {
330 pub fn serialize_quad(&mut self, quad: QuadRef<'_>) -> Result<(), FormatError> {
332 self.buffer.push(quad.into());
333 Ok(())
334 }
335
336 pub fn finish(mut self) -> Result<W, FormatError> {
338 self.serializer
339 .serialize_quads(&self.buffer, &mut self.writer)?;
340 Ok(self.writer)
341 }
342}
343
344impl<W: Write> super::serializer::QuadSerializer<W> for TriGWriter<W> {
346 fn serialize_quad(&mut self, quad: QuadRef<'_>) -> super::serializer::QuadSerializeResult {
347 TriGWriter::serialize_quad(self, quad)
348 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))
349 }
350
351 fn finish(self: Box<Self>) -> super::error::SerializeResult<W> {
352 TriGWriter::finish(*self).map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))
353 }
354}
355
356#[cfg(test)]
357mod tests {
358 use super::*;
359 use crate::model::{NamedNode, Object, Quad, Subject, Triple};
360
361 #[test]
362 fn test_trig_serialize_default_graph() {
363 let serializer = TriGSerializer::new();
364 let mut writer = Vec::new();
365
366 let triple = Triple::new(
367 Subject::NamedNode(NamedNode::new("http://example.org/subject").expect("valid IRI")),
368 NamedNode::new("http://example.org/predicate").expect("valid IRI"),
369 Object::NamedNode(NamedNode::new("http://example.org/object").expect("valid IRI")),
370 );
371
372 let quads = vec![Quad::from(triple)];
373 serializer
374 .serialize_quads(&quads, &mut writer)
375 .expect("operation should succeed");
376
377 let output = String::from_utf8(writer).expect("bytes should be valid UTF-8");
378 assert!(output.contains("<http://example.org/subject>"));
379 assert!(output.contains("<http://example.org/predicate>"));
380 assert!(output.contains("<http://example.org/object>"));
381 }
382
383 #[test]
384 fn test_trig_serialize_named_graph() {
385 let serializer = TriGSerializer::new();
386 let mut writer = Vec::new();
387
388 let quad = Quad::new(
389 Subject::NamedNode(NamedNode::new("http://example.org/subject").expect("valid IRI")),
390 NamedNode::new("http://example.org/predicate").expect("valid IRI"),
391 Object::NamedNode(NamedNode::new("http://example.org/object").expect("valid IRI")),
392 GraphName::NamedNode(NamedNode::new("http://example.org/graph").expect("valid IRI")),
393 );
394
395 let quads = vec![quad];
396 serializer
397 .serialize_quads(&quads, &mut writer)
398 .expect("operation should succeed");
399
400 let output = String::from_utf8(writer).expect("bytes should be valid UTF-8");
401 assert!(output.contains("<http://example.org/graph>"));
402 assert!(output.contains("{"));
403 assert!(output.contains("}"));
404 }
405}