use sophia_api::prefix::{Prefix, PrefixMap, PrefixMapPair};
use sophia_api::serializer::{Stringifier, TripleSerializer};
use sophia_api::source::{SinkError, SourceError, StreamResult, TripleSource};
use sophia_api::term::{SimpleTerm, Term};
use sophia_api::triple::Triple;
use sophia_iri::Iri;
use std::io;
use super::_pretty::*;
use super::_streaming::StreamingSerializerState;
#[derive(Clone, Debug)]
pub struct TurtleConfig {
pub(super) pretty: bool,
pub(super) prefix_map: Vec<PrefixMapPair>,
pub(super) indentation: String,
}
impl TurtleConfig {
#[inline]
pub const fn pretty(&self) -> bool {
self.pretty
}
#[inline]
pub fn prefix_map(&self) -> &[PrefixMapPair] {
&self.prefix_map
}
#[inline]
pub fn indentation(&self) -> &str {
&self.indentation
}
pub fn new() -> Self {
let pretty = false;
let prefix_map = Self::default_prefix_map();
let indentation = " ".to_string();
Self {
pretty,
prefix_map,
indentation,
}
}
#[must_use]
pub const fn with_pretty(mut self, b: bool) -> Self {
self.pretty = b;
self
}
#[must_use]
pub fn with_prefix_map<P: PrefixMap + ?Sized>(self, pm: &P) -> Self {
self.with_own_prefix_map(pm.to_vec())
}
#[must_use]
pub fn with_own_prefix_map(mut self, pm: Vec<PrefixMapPair>) -> Self {
self.prefix_map = pm;
self
}
#[must_use]
pub fn with_indentation<T: ToString>(mut self, indentation: T) -> Self {
let indentation = indentation.to_string();
assert!(indentation.chars().all(char::is_whitespace));
self.indentation = indentation;
self
}
#[inline]
pub fn default_prefix_map() -> Vec<PrefixMapPair> {
vec![
(
Prefix::new_unchecked("rdf".into()),
Iri::new_unchecked("http://www.w3.org/1999/02/22-rdf-syntax-ns#".into()),
),
(
Prefix::new_unchecked("rdfs".into()),
Iri::new_unchecked("http://www.w3.org/2000/01/rdf-schema#".into()),
),
(
Prefix::new_unchecked("xsd".into()),
Iri::new_unchecked("http://www.w3.org/2001/XMLSchema#".into()),
),
]
}
}
impl Default for TurtleConfig {
fn default() -> Self {
Self::new()
}
}
pub struct TurtleSerializer<W> {
pub(super) config: TurtleConfig,
pub(super) write: W,
}
impl<W> TurtleSerializer<W>
where
W: io::Write,
{
#[inline]
pub fn new(write: W) -> Self {
Self::new_with_config(write, TurtleConfig::default())
}
#[inline]
pub const fn new_with_config(write: W, config: TurtleConfig) -> Self {
Self { config, write }
}
#[inline]
pub const fn config(&self) -> &TurtleConfig {
&self.config
}
}
impl<W> TripleSerializer for TurtleSerializer<W>
where
W: io::Write,
{
type Error = io::Error;
fn serialize_triples<TS>(
&mut self,
mut source: TS,
) -> StreamResult<&mut Self, TS::Error, Self::Error>
where
TS: TripleSource,
{
if self.config.pretty {
let mut dataset = PrettifiableDataset::new();
let default = None as Option<SimpleTerm>;
source
.for_each_triple(|t| {
let spo = t.spo().map(Term::into_term);
dataset.insert((default.clone(), spo));
})
.map_err(SourceError)?;
prettify(dataset, &mut self.write, &self.config, "").map_err(SinkError)?;
} else {
for (prefix, ns) in &self.config.prefix_map {
writeln!(&mut self.write, "PREFIX {}: <{ns}>", prefix.as_str())
.map_err(SinkError)?;
}
let mut state = StreamingSerializerState::new(&mut self.write, &self.config);
source.try_for_each_triple(|t| state.write_asserted_triple(t))?;
if state.has_s() {
state.write_all(b".\n").map_err(SinkError)?;
}
}
Ok(self)
}
}
impl TurtleSerializer<Vec<u8>> {
#[inline]
pub fn new_stringifier() -> Self {
Self::new(Vec::new())
}
#[inline]
pub const fn new_stringifier_with_config(config: TurtleConfig) -> Self {
Self::new_with_config(Vec::new(), config)
}
}
impl Stringifier for TurtleSerializer<Vec<u8>> {
fn as_utf8(&self) -> &[u8] {
&self.write[..]
}
}
#[cfg(test)]
mod test;