1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use constants;
use graph::*;
use std::fmt::Display;
use std::io::{Result, Write};
use std::marker::PhantomData;

struct NTriplesWriter<'a, 'g, W: 'a, G: 'g>
where
    W: Write,
    G: Graph<'g>,
{
    buffer: Vec<u8>,
    writer: &'a mut W,
    xsd_string: Option<<<G as Graph<'g>>::LiteralPtr as LiteralPtr<'g>>::DatatypePtr>,
    phantom: PhantomData<&'g u8>,
}

/// write an RDF 1.1 N-Triples file in canonical form
pub fn write_ntriples<'g, G: 'g, T: 'g, I, W>(
    triples: I,
    graph: &'g G,
    writer: &mut W,
) -> Result<()>
where
    T: Triple<'g, G::BlankNodePtr, G::IRIPtr, G::LiteralPtr>,
    G: Graph<'g>,
    <G as Graph<'g>>::BlankNodePtr: Display,
    I: Iterator<Item = T>,
    W: Write,
{
    let mut writer = NTriplesWriter::<_, G> {
        buffer: Vec::new(),
        writer,
        xsd_string: graph.find_datatype(constants::XSD_STRING),
        phantom: PhantomData,
    };
    for triple in triples {
        writer.write_ntriple(&triple)?;
    }
    Ok(())
}

impl<'a, 'g, W: 'a, G: 'g> NTriplesWriter<'a, 'g, W, G>
where
    W: Write,
    G: Graph<'g>,
    <G as Graph<'g>>::BlankNodePtr: Display,
{
    fn write_iri(&mut self, iri: &str) -> Result<()> {
        self.writer.write_all(b"<")?;
        self.buffer.clear();
        for b in iri.as_bytes() {
            if *b < 20 || b"<>\"{}|^`\\".contains(b) {
                write!(&mut self.buffer, "\\u00{:X} ", *b).unwrap();
            } else {
                self.buffer.push(*b);
            }
        }
        self.writer.write_all(&self.buffer[..])?;
        self.writer.write_all(b">")
    }
    fn write_blank_node(&mut self, blank_node: G::BlankNodePtr) -> Result<()> {
        self.writer.write_all(b"_:")?;
        write!(self.writer, "{}", blank_node)?;
        Ok(())
    }
    fn write_literal_value(&mut self, value: &str) -> Result<()> {
        self.buffer.clear();
        for b in value.as_bytes() {
            if *b == 0x22 || *b == 0x5C || *b == 0x0A || *b == 0x0D {
                self.buffer.push(b'\\');
            }
            self.buffer.push(*b);
        }
        self.writer.write_all(&self.buffer[..])
    }
    fn write_literal(&mut self, literal: &G::LiteralPtr) -> Result<()> {
        self.writer.write_all(b"\"")?;
        self.write_literal_value(literal.as_str())?;
        self.writer.write_all(b"\"")?;
        if let Some(langtag) = literal.language() {
            self.writer.write_all(b"@")?;
            self.writer.write_all(langtag.as_bytes())?;
        } else if Some(literal.datatype()) != self.xsd_string {
            self.writer.write_all(b"^^")?;
            self.write_iri(literal.datatype_str())?;
        }
        Ok(())
    }
    fn write_subject(
        &mut self,
        subject: BlankNodeOrIRI<'g, G::BlankNodePtr, G::IRIPtr>,
    ) -> Result<()> {
        match subject {
            BlankNodeOrIRI::BlankNode(blank_node, _) => self.write_blank_node(blank_node),
            BlankNodeOrIRI::IRI(iri) => self.write_iri(iri.as_str()),
        }
    }
    fn write_predicate(&mut self, predicate: &str) -> Result<()> {
        self.write_iri(predicate)
    }
    fn write_object(
        &mut self,
        object: Resource<'g, G::BlankNodePtr, G::IRIPtr, G::LiteralPtr>,
    ) -> Result<()> {
        match object {
            Resource::BlankNode(blank_node, _) => self.write_blank_node(blank_node),
            Resource::IRI(iri) => self.write_iri(iri.as_str()),
            Resource::Literal(literal) => self.write_literal(&literal),
        }
    }
    fn write_ntriple<T: 'g>(&mut self, triple: &T) -> Result<()>
    where
        T: Triple<'g, G::BlankNodePtr, G::IRIPtr, G::LiteralPtr>,
    {
        self.write_subject(triple.subject())?;
        self.writer.write_all(b" ")?;
        self.write_predicate(triple.predicate().as_str())?;
        self.writer.write_all(b" ")?;
        self.write_object(triple.object())?;
        self.writer.write_all(b" .\n")
    }
}