1use super::error::FormatError;
7pub use super::error::SerializeResult;
8use super::format::RdfFormat;
9use crate::model::{Quad, QuadRef, Triple, TripleRef};
10use std::collections::HashMap;
11use std::io::Write;
12
13pub type QuadSerializeResult = SerializeResult<()>;
15
16pub struct WriterQuadSerializer<W: Write> {
18 inner: Box<dyn QuadSerializer<W>>,
19}
20
21impl<W: Write> WriterQuadSerializer<W> {
22 pub fn new(serializer: Box<dyn QuadSerializer<W>>) -> Self {
24 Self { inner: serializer }
25 }
26
27 pub fn serialize_quad<'a>(&mut self, quad: impl Into<QuadRef<'a>>) -> QuadSerializeResult {
29 self.inner.serialize_quad(quad.into())
30 }
31
32 pub fn serialize_triple<'a>(
34 &mut self,
35 triple: impl Into<TripleRef<'a>>,
36 ) -> QuadSerializeResult {
37 let quad = triple.into().in_graph(None);
38 self.serialize_quad(quad)
39 }
40
41 pub fn serialize_quads<I>(&mut self, quads: I) -> QuadSerializeResult
43 where
44 I: IntoIterator,
45 I::Item: Into<QuadRef<'static>>,
46 {
47 for quad in quads {
48 self.inner.serialize_quad(quad.into())?;
49 }
50 Ok(())
51 }
52
53 pub fn finish(self) -> SerializeResult<W> {
55 self.inner.finish()
56 }
57}
58
59pub trait QuadSerializer<W: Write> {
61 fn serialize_quad(&mut self, quad: QuadRef<'_>) -> QuadSerializeResult;
63
64 fn finish(self: Box<Self>) -> SerializeResult<W>;
66}
67
68pub trait QuadSerializerExt<W: Write>: QuadSerializer<W> {
70 fn serialize_quads<I>(&mut self, quads: I) -> QuadSerializeResult
72 where
73 I: IntoIterator,
74 I::Item: Into<QuadRef<'static>>,
75 {
76 for quad in quads {
77 self.serialize_quad(quad.into())?;
78 }
79 Ok(())
80 }
81}
82
83impl<W: Write, T: QuadSerializer<W>> QuadSerializerExt<W> for T {}
85
86pub struct RdfSerializer {
88 format: RdfFormat,
89 base_iri: Option<String>,
90 prefixes: HashMap<String, String>,
91 pretty: bool,
92}
93
94impl RdfSerializer {
95 pub fn new(format: RdfFormat) -> Self {
97 Self {
98 format,
99 base_iri: None,
100 prefixes: HashMap::new(),
101 pretty: false,
102 }
103 }
104
105 pub fn with_base_iri(mut self, base_iri: impl Into<String>) -> Self {
107 self.base_iri = Some(base_iri.into());
108 self
109 }
110
111 pub fn with_prefix(mut self, prefix: impl Into<String>, iri: impl Into<String>) -> Self {
113 self.prefixes.insert(prefix.into(), iri.into());
114 self
115 }
116
117 pub fn pretty(mut self) -> Self {
119 self.pretty = true;
120 self
121 }
122
123 pub fn for_writer<W: Write + 'static>(self, writer: W) -> WriterQuadSerializer<W> {
125 match self.format {
126 RdfFormat::Turtle => self.create_turtle_serializer(writer),
127 RdfFormat::NTriples => self.create_ntriples_serializer(writer),
128 RdfFormat::NQuads => self.create_nquads_serializer(writer),
129 RdfFormat::TriG => self.create_trig_serializer(writer),
130 RdfFormat::RdfXml => self.create_rdfxml_serializer(writer),
131 RdfFormat::JsonLd { .. } => self.create_jsonld_serializer(writer),
132 RdfFormat::N3 => self.create_n3_serializer(writer),
133 }
134 }
135
136 pub fn format(&self) -> RdfFormat {
138 self.format.clone()
139 }
140
141 pub fn base_iri(&self) -> Option<&str> {
143 self.base_iri.as_deref()
144 }
145
146 pub fn prefixes(&self) -> &HashMap<String, String> {
148 &self.prefixes
149 }
150
151 pub fn is_pretty(&self) -> bool {
153 self.pretty
154 }
155
156 fn create_turtle_serializer<W: Write + 'static>(self, writer: W) -> WriterQuadSerializer<W> {
159 let mut turtle_serializer = super::turtle::TurtleSerializer::new();
161
162 if let Some(base) = self.base_iri {
164 turtle_serializer = turtle_serializer.with_base_iri(&base);
165 }
166 for (prefix, iri) in self.prefixes {
167 turtle_serializer = turtle_serializer.with_prefix(&prefix, &iri);
168 }
169 if self.pretty {
170 turtle_serializer = turtle_serializer.pretty();
171 }
172
173 WriterQuadSerializer::new(Box::new(turtle_serializer.for_writer(writer)))
174 }
175
176 fn create_ntriples_serializer<W: Write + 'static>(self, writer: W) -> WriterQuadSerializer<W> {
177 let ntriples_serializer = super::ntriples::NTriplesSerializer::new().for_writer(writer);
179 WriterQuadSerializer::new(Box::new(ntriples_serializer))
180 }
181
182 fn create_nquads_serializer<W: Write + 'static>(self, writer: W) -> WriterQuadSerializer<W> {
183 let nquads_serializer = super::nquads::NQuadsSerializer::new().for_writer(writer);
185 WriterQuadSerializer::new(Box::new(nquads_serializer))
186 }
187
188 fn create_trig_serializer<W: Write + 'static>(self, writer: W) -> WriterQuadSerializer<W> {
189 let mut trig_serializer = super::trig::TriGSerializer::new();
191
192 if let Some(base) = self.base_iri {
194 trig_serializer = trig_serializer.with_base_iri(&base);
195 }
196 for (prefix, iri) in self.prefixes {
197 trig_serializer = trig_serializer.with_prefix(&prefix, &iri);
198 }
199 if self.pretty {
200 trig_serializer = trig_serializer.pretty();
201 }
202
203 WriterQuadSerializer::new(Box::new(trig_serializer.for_writer(writer)))
204 }
205
206 fn create_rdfxml_serializer<W: Write + 'static>(self, writer: W) -> WriterQuadSerializer<W> {
207 let mut rdfxml_serializer = super::rdfxml::RdfXmlSerializer::new();
209
210 if self.pretty {
212 rdfxml_serializer = rdfxml_serializer.pretty();
213 }
214
215 WriterQuadSerializer::new(Box::new(rdfxml_serializer.for_writer(writer)))
216 }
217
218 fn create_jsonld_serializer<W: Write + 'static>(self, writer: W) -> WriterQuadSerializer<W> {
219 let mut jsonld_serializer = super::jsonld::JsonLdSerializer::new();
221
222 if self.pretty {
224 jsonld_serializer = jsonld_serializer.pretty();
225 }
226
227 WriterQuadSerializer::new(Box::new(jsonld_serializer.for_writer(writer)))
228 }
229
230 fn create_n3_serializer<W: Write + 'static>(self, writer: W) -> WriterQuadSerializer<W> {
231 let mut n3_serializer = super::n3::N3Serializer::new();
233
234 if let Some(base) = self.base_iri {
236 n3_serializer = n3_serializer.with_base_iri(&base);
237 }
238 for (prefix, iri) in self.prefixes {
239 n3_serializer = n3_serializer.with_prefix(&prefix, &iri);
240 }
241 if self.pretty {
242 n3_serializer = n3_serializer.pretty();
243 }
244
245 WriterQuadSerializer::new(Box::new(n3_serializer.for_writer(writer)))
246 }
247}
248
249impl Default for RdfSerializer {
250 fn default() -> Self {
251 Self::new(RdfFormat::default())
252 }
253}
254
255#[derive(Debug, Clone)]
257pub struct SerializeConfig {
258 pub compact: bool,
260 pub indent: String,
262 pub line_ending: LineEnding,
264 pub max_line_length: Option<usize>,
266 pub sort_output: bool,
268 pub include_comments: bool,
270 pub validate_output: bool,
272}
273
274#[derive(Debug, Clone, PartialEq, Eq)]
276pub enum LineEnding {
277 Unix,
279 Windows,
281 Mac,
283 Platform,
285}
286
287impl Default for SerializeConfig {
288 fn default() -> Self {
289 Self {
290 compact: false,
291 indent: " ".to_string(),
292 line_ending: LineEnding::Platform,
293 max_line_length: None,
294 sort_output: false,
295 include_comments: false,
296 validate_output: true,
297 }
298 }
299}
300
301impl LineEnding {
302 pub fn as_str(&self) -> &'static str {
304 match self {
305 Self::Unix => "\n",
306 Self::Windows => "\r\n",
307 Self::Mac => "\r",
308 Self::Platform => {
309 #[cfg(windows)]
310 return "\r\n";
311 #[cfg(not(windows))]
312 return "\n";
313 }
314 }
315 }
316}
317
318pub struct PostProcessingWriter<W: Write> {
322 inner: W,
323 buffer: Vec<u8>,
324 line_ending: LineEnding,
325 sort_output: bool,
326}
327
328impl<W: Write> PostProcessingWriter<W> {
329 fn new(inner: W, line_ending: LineEnding, sort_output: bool) -> Self {
330 Self {
331 inner,
332 buffer: Vec::new(),
333 line_ending,
334 sort_output,
335 }
336 }
337
338 fn finalize(mut self) -> std::io::Result<W> {
342 let text = String::from_utf8_lossy(&self.buffer);
343 let mut lines: Vec<&str> = text.lines().collect();
346 if self.sort_output {
347 lines.sort_unstable();
348 }
349 let ending = self.line_ending.as_str();
350 for line in &lines {
351 self.inner.write_all(line.as_bytes())?;
352 self.inner.write_all(ending.as_bytes())?;
353 }
354 self.inner.flush()?;
355 Ok(self.inner)
356 }
357}
358
359impl<W: Write> Write for PostProcessingWriter<W> {
360 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
361 self.buffer.extend_from_slice(buf);
362 Ok(buf.len())
363 }
364
365 fn flush(&mut self) -> std::io::Result<()> {
366 Ok(())
370 }
371}
372
373pub enum ConfiguredQuadSerializer<W: Write> {
379 Direct(WriterQuadSerializer<W>),
381 Buffered(WriterQuadSerializer<PostProcessingWriter<W>>),
384}
385
386impl<W: Write + 'static> ConfiguredQuadSerializer<W> {
387 pub fn serialize_quad<'a>(&mut self, quad: impl Into<QuadRef<'a>>) -> QuadSerializeResult {
389 match self {
390 Self::Direct(s) => s.serialize_quad(quad),
391 Self::Buffered(s) => s.serialize_quad(quad),
392 }
393 }
394
395 pub fn serialize_triple<'a>(
397 &mut self,
398 triple: impl Into<TripleRef<'a>>,
399 ) -> QuadSerializeResult {
400 match self {
401 Self::Direct(s) => s.serialize_triple(triple),
402 Self::Buffered(s) => s.serialize_triple(triple),
403 }
404 }
405
406 pub fn finish(self) -> SerializeResult<W> {
409 match self {
410 Self::Direct(s) => s.finish(),
411 Self::Buffered(s) => {
412 let post_writer = s.finish()?;
413 post_writer.finalize()
414 }
415 }
416 }
417}
418
419pub struct ConfigurableSerializer {
421 serializer: RdfSerializer,
422 config: SerializeConfig,
423}
424
425impl ConfigurableSerializer {
426 pub fn new(format: RdfFormat, config: SerializeConfig) -> Self {
428 Self {
429 serializer: RdfSerializer::new(format),
430 config,
431 }
432 }
433
434 pub fn with_base_iri(mut self, base_iri: impl Into<String>) -> Self {
436 self.serializer = self.serializer.with_base_iri(base_iri);
437 self
438 }
439
440 pub fn with_prefix(mut self, prefix: impl Into<String>, iri: impl Into<String>) -> Self {
442 self.serializer = self.serializer.with_prefix(prefix, iri);
443 self
444 }
445
446 pub fn for_writer<W: Write + 'static>(self, writer: W) -> ConfiguredQuadSerializer<W> {
462 let mut serializer = self.serializer;
464
465 if !self.config.compact {
467 serializer = serializer.pretty();
468 }
469
470 let needs_post_processing =
471 self.config.sort_output || self.config.line_ending != LineEnding::Platform;
472
473 if needs_post_processing {
474 let post_writer =
475 PostProcessingWriter::new(writer, self.config.line_ending, self.config.sort_output);
476 ConfiguredQuadSerializer::Buffered(serializer.for_writer(post_writer))
477 } else {
478 ConfiguredQuadSerializer::Direct(serializer.for_writer(writer))
479 }
480 }
481
482 pub fn config(&self) -> &SerializeConfig {
484 &self.config
485 }
486
487 pub fn serializer(&self) -> &RdfSerializer {
489 &self.serializer
490 }
491}
492
493pub mod simple {
495 use super::*;
496
497 pub fn serialize_triples_to_string(
499 triples: &[Triple],
500 format: RdfFormat,
501 ) -> Result<String, FormatError> {
502 let buffer = Vec::new();
503 let mut serializer = RdfSerializer::new(format).for_writer(buffer);
504 for triple in triples {
505 serializer.serialize_triple(triple.as_ref())?;
506 }
507 let buffer = serializer.finish()?;
508 String::from_utf8(buffer).map_err(|e| FormatError::invalid_data(e.to_string()))
509 }
510
511 pub fn serialize_quads_to_string(
513 quads: &[Quad],
514 format: RdfFormat,
515 ) -> Result<String, FormatError> {
516 let buffer = Vec::new();
517 let mut serializer = RdfSerializer::new(format).for_writer(buffer);
518 for quad in quads {
519 serializer.serialize_quad(quad.as_ref())?;
520 }
521 let buffer = serializer.finish()?;
522 String::from_utf8(buffer).map_err(|e| FormatError::invalid_data(e.to_string()))
523 }
524
525 pub fn serialize_turtle(triples: &[Triple]) -> Result<String, FormatError> {
527 serialize_triples_to_string(triples, RdfFormat::Turtle)
528 }
529
530 pub fn serialize_ntriples(triples: &[Triple]) -> Result<String, FormatError> {
532 serialize_triples_to_string(triples, RdfFormat::NTriples)
533 }
534
535 pub fn serialize_nquads(quads: &[Quad]) -> Result<String, FormatError> {
537 serialize_quads_to_string(quads, RdfFormat::NQuads)
538 }
539
540 pub fn serialize_trig(quads: &[Quad]) -> Result<String, FormatError> {
542 serialize_quads_to_string(quads, RdfFormat::TriG)
543 }
544}
545
546#[cfg(test)]
547mod tests {
548 use super::*;
549
550 #[test]
551 fn test_serializer_creation() {
552 let serializer = RdfSerializer::new(RdfFormat::Turtle);
553 assert_eq!(serializer.format(), RdfFormat::Turtle);
554 assert!(serializer.base_iri().is_none());
555 assert!(serializer.prefixes().is_empty());
556 assert!(!serializer.is_pretty());
557 }
558
559 #[test]
560 fn test_serializer_configuration() {
561 let serializer = RdfSerializer::new(RdfFormat::Turtle)
562 .with_base_iri("http://example.org/")
563 .with_prefix("ex", "http://example.org/ns#")
564 .pretty();
565
566 assert_eq!(serializer.base_iri(), Some("http://example.org/"));
567 assert_eq!(
568 serializer.prefixes().get("ex"),
569 Some(&"http://example.org/ns#".to_string())
570 );
571 assert!(serializer.is_pretty());
572 }
573
574 #[test]
575 fn test_configurable_serializer() {
576 let config = SerializeConfig {
577 compact: true,
578 sort_output: true,
579 ..Default::default()
580 };
581
582 let serializer = ConfigurableSerializer::new(RdfFormat::NQuads, config);
583 assert!(serializer.config().compact);
584 assert!(serializer.config().sort_output);
585 }
586
587 #[test]
588 fn test_serialize_config_default() {
589 let config = SerializeConfig::default();
590 assert!(!config.compact);
591 assert_eq!(config.indent, " ");
592 assert_eq!(config.line_ending, LineEnding::Platform);
593 assert_eq!(config.max_line_length, None);
594 assert!(!config.sort_output);
595 assert!(!config.include_comments);
596 assert!(config.validate_output);
597 }
598
599 #[test]
600 fn test_line_ending() {
601 assert_eq!(LineEnding::Unix.as_str(), "\n");
602 assert_eq!(LineEnding::Windows.as_str(), "\r\n");
603 assert_eq!(LineEnding::Mac.as_str(), "\r");
604 }
606
607 #[test]
608 fn test_configurable_serializer_sort_output_applied() {
609 use crate::model::{NamedNode, Triple};
610
611 let s = |n: &str| NamedNode::new(format!("http://example.org/{n}")).unwrap();
612 let triples = vec![
613 Triple::new(s("c"), s("p"), s("z")),
614 Triple::new(s("a"), s("p"), s("z")),
615 Triple::new(s("b"), s("p"), s("z")),
616 ];
617
618 let config = SerializeConfig {
619 sort_output: true,
620 line_ending: LineEnding::Unix,
621 ..Default::default()
622 };
623 let mut serializer =
624 ConfigurableSerializer::new(RdfFormat::NTriples, config).for_writer(Vec::new());
625 for triple in &triples {
626 serializer.serialize_triple(triple.as_ref()).unwrap();
627 }
628 let buffer = serializer.finish().unwrap();
629 let output = String::from_utf8(buffer).unwrap();
630 let lines: Vec<&str> = output.lines().collect();
631
632 let mut expected = lines.clone();
634 expected.sort_unstable();
635 assert_eq!(lines, expected);
636 assert!(lines[0].contains("/a>"));
637 assert!(lines[2].contains("/c>"));
638 assert!(!output.contains('\r'));
640 }
641
642 #[test]
643 fn test_configurable_serializer_windows_line_ending() {
644 use crate::model::{NamedNode, Triple};
645
646 let s = |n: &str| NamedNode::new(format!("http://example.org/{n}")).unwrap();
647 let triple = Triple::new(s("s"), s("p"), s("o"));
648
649 let config = SerializeConfig {
650 line_ending: LineEnding::Windows,
651 ..Default::default()
652 };
653 let mut serializer =
654 ConfigurableSerializer::new(RdfFormat::NTriples, config).for_writer(Vec::new());
655 serializer.serialize_triple(triple.as_ref()).unwrap();
656 let buffer = serializer.finish().unwrap();
657 let output = String::from_utf8(buffer).unwrap();
658 assert!(output.ends_with("\r\n"));
659 }
660}