use std::io::Write;
use std::path::Path;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ExportFormat {
Turtle,
NTriples,
RdfXml,
JsonLd,
TriG,
NQuads,
}
impl ExportFormat {
pub fn extension(&self) -> &'static str {
match self {
ExportFormat::Turtle => "ttl",
ExportFormat::NTriples => "nt",
ExportFormat::RdfXml => "rdf",
ExportFormat::JsonLd => "jsonld",
ExportFormat::TriG => "trig",
ExportFormat::NQuads => "nq",
}
}
pub fn mime_type(&self) -> &'static str {
match self {
ExportFormat::Turtle => "text/turtle",
ExportFormat::NTriples => "application/n-triples",
ExportFormat::RdfXml => "application/rdf+xml",
ExportFormat::JsonLd => "application/ld+json",
ExportFormat::TriG => "application/trig",
ExportFormat::NQuads => "application/n-quads",
}
}
}
impl std::fmt::Display for ExportFormat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ExportFormat::Turtle => write!(f, "Turtle"),
ExportFormat::NTriples => write!(f, "N-Triples"),
ExportFormat::RdfXml => write!(f, "RDF/XML"),
ExportFormat::JsonLd => write!(f, "JSON-LD"),
ExportFormat::TriG => write!(f, "TriG"),
ExportFormat::NQuads => write!(f, "N-Quads"),
}
}
}
#[derive(Debug, Clone)]
pub struct ExportConfig {
pub format: ExportFormat,
pub pretty: bool,
pub base_uri: Option<String>,
pub use_prefixes: bool,
pub compression: Option<u8>,
}
impl Default for ExportConfig {
fn default() -> Self {
Self {
format: ExportFormat::Turtle,
pretty: true,
base_uri: None,
use_prefixes: true,
compression: None,
}
}
}
pub struct Exporter {
config: ExportConfig,
}
impl Exporter {
pub fn new() -> Self {
Self {
config: ExportConfig::default(),
}
}
pub fn with_config(config: ExportConfig) -> Self {
Self { config }
}
pub fn with_format(mut self, format: ExportFormat) -> Self {
self.config.format = format;
self
}
pub fn with_pretty(mut self, pretty: bool) -> Self {
self.config.pretty = pretty;
self
}
pub fn with_base_uri(mut self, base_uri: Option<String>) -> Self {
self.config.base_uri = base_uri;
self
}
pub fn export_to_writer<W: Write + 'static>(
&self,
data: &str,
writer: W,
) -> Result<(), Box<dyn std::error::Error>> {
use oxirs_core::format::{JsonLdProfileSet, RdfFormat as CoreFormat, RdfSerializer};
use oxirs_core::parser::{Parser, RdfFormat as ParserFormat};
let input_format = if data.lines().any(|line| {
let parts: Vec<&str> = line.split_whitespace().collect();
parts.len() == 5 && parts[4] == "."
}) {
ParserFormat::NQuads
} else {
ParserFormat::NTriples
};
let parser = Parser::new(input_format);
let quads = parser.parse_str_to_quads(data)?;
let output_format = match self.config.format {
ExportFormat::Turtle => CoreFormat::Turtle,
ExportFormat::NTriples => CoreFormat::NTriples,
ExportFormat::RdfXml => CoreFormat::RdfXml,
ExportFormat::JsonLd => CoreFormat::JsonLd {
profile: JsonLdProfileSet::empty(),
},
ExportFormat::TriG => CoreFormat::TriG,
ExportFormat::NQuads => CoreFormat::NQuads,
};
let mut serializer = RdfSerializer::new(output_format);
if self.config.use_prefixes {
serializer = serializer
.with_prefix("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#")
.with_prefix("rdfs", "http://www.w3.org/2000/01/rdf-schema#")
.with_prefix("xsd", "http://www.w3.org/2001/XMLSchema#")
.with_prefix("owl", "http://www.w3.org/2002/07/owl#");
}
if let Some(base) = &self.config.base_uri {
serializer = serializer.with_base_iri(base);
}
if self.config.pretty {
serializer = serializer.pretty();
}
let mut writer_serializer = serializer.for_writer(writer);
for quad in &quads {
writer_serializer.serialize_quad(quad.as_ref())?;
}
writer_serializer.finish()?;
Ok(())
}
pub fn export_to_file<P: AsRef<Path>>(
&self,
data: &str,
path: P,
) -> Result<(), Box<dyn std::error::Error>> {
let file = std::fs::File::create(path)?;
self.export_to_writer(data, file)
}
pub fn export_to_string(&self, data: &str) -> Result<String, Box<dyn std::error::Error>> {
use oxirs_core::format::{JsonLdProfileSet, RdfFormat as CoreFormat, RdfSerializer};
use oxirs_core::parser::{Parser, RdfFormat as ParserFormat};
use std::io::Cursor;
let input_format = if data.lines().any(|line| {
let parts: Vec<&str> = line.split_whitespace().collect();
parts.len() == 5 && parts[4] == "."
}) {
ParserFormat::NQuads
} else {
ParserFormat::NTriples
};
let parser = Parser::new(input_format);
let quads = parser.parse_str_to_quads(data)?;
let output_format = match self.config.format {
ExportFormat::Turtle => CoreFormat::Turtle,
ExportFormat::NTriples => CoreFormat::NTriples,
ExportFormat::RdfXml => CoreFormat::RdfXml,
ExportFormat::JsonLd => CoreFormat::JsonLd {
profile: JsonLdProfileSet::empty(),
},
ExportFormat::TriG => CoreFormat::TriG,
ExportFormat::NQuads => CoreFormat::NQuads,
};
let mut serializer = RdfSerializer::new(output_format);
if self.config.use_prefixes {
serializer = serializer
.with_prefix("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#")
.with_prefix("rdfs", "http://www.w3.org/2000/01/rdf-schema#")
.with_prefix("xsd", "http://www.w3.org/2001/XMLSchema#")
.with_prefix("owl", "http://www.w3.org/2002/07/owl#");
}
if let Some(base) = &self.config.base_uri {
serializer = serializer.with_base_iri(base);
}
if self.config.pretty {
serializer = serializer.pretty();
}
let cursor = Cursor::new(Vec::new());
let mut writer_serializer = serializer.for_writer(cursor);
for quad in &quads {
writer_serializer.serialize_quad(quad.as_ref())?;
}
let cursor = writer_serializer.finish()?;
let buffer = cursor.into_inner();
Ok(String::from_utf8(buffer)?)
}
pub fn config(&self) -> &ExportConfig {
&self.config
}
pub fn set_config(&mut self, config: ExportConfig) {
self.config = config;
}
}
impl Default for Exporter {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_export_format_extension() {
assert_eq!(ExportFormat::Turtle.extension(), "ttl");
assert_eq!(ExportFormat::NTriples.extension(), "nt");
assert_eq!(ExportFormat::JsonLd.extension(), "jsonld");
}
#[test]
fn test_export_format_mime_type() {
assert_eq!(ExportFormat::Turtle.mime_type(), "text/turtle");
assert_eq!(ExportFormat::JsonLd.mime_type(), "application/ld+json");
}
#[test]
fn test_exporter_creation() {
let exporter = Exporter::new();
assert_eq!(exporter.config().format, ExportFormat::Turtle);
assert!(exporter.config().pretty);
}
#[test]
fn test_exporter_with_format() {
let exporter = Exporter::new().with_format(ExportFormat::JsonLd);
assert_eq!(exporter.config().format, ExportFormat::JsonLd);
}
#[test]
fn test_export_to_string() {
let exporter = Exporter::new().with_format(ExportFormat::NTriples);
let test_data = "<http://example.org/subject> <http://example.org/predicate> \"object\" .";
let result = exporter.export_to_string(test_data).unwrap();
assert!(result.contains("http://example.org/subject"));
}
}