use anyhow::Result;
use std::io::Read;
use std::path::Path;
use reqwest::header::CONTENT_TYPE;
use oxigraph::io::write::GraphSerializer;
use oxigraph::io::{GraphFormat, GraphParser};
use oxigraph::model::graph::Graph as OxigraphGraph;
use oxigraph::model::Dataset;
use oxigraph::model::TripleRef;
use std::io::BufReader;
use log::{debug, info};
pub fn write_dataset_to_file(dataset: &Dataset, file: &str) -> Result<()> {
info!(
"Writing dataset to file: {} with length {}",
file,
dataset.len()
);
let mut file = std::fs::File::create(file)?;
let mut serializer =
GraphSerializer::from_format(GraphFormat::Turtle).triple_writer(&mut file)?;
for quad in dataset.iter() {
serializer.write(TripleRef {
subject: quad.subject,
predicate: quad.predicate,
object: quad.object,
})?;
}
serializer.finish()?;
Ok(())
}
pub fn read_file(file: &Path) -> Result<OxigraphGraph> {
debug!("Reading file: {}", file.to_str().unwrap());
let filename = file;
let file = std::fs::File::open(file)?;
let content: BufReader<_> = BufReader::new(file);
let content_type = filename.extension().and_then(|ext| ext.to_str());
let content_type = content_type.and_then(|ext| match ext {
"ttl" => Some(GraphFormat::Turtle),
"xml" => Some(GraphFormat::RdfXml),
"n3" => Some(GraphFormat::Turtle),
"nt" => Some(GraphFormat::NTriples),
_ => None,
});
let parser = GraphParser::from_format(content_type.unwrap_or(GraphFormat::Turtle));
let mut graph = OxigraphGraph::new();
let triples = parser.read_triples(content)?;
for triple in triples {
graph.insert(&triple?);
}
Ok(graph)
}
pub fn read_url(file: &str) -> Result<OxigraphGraph> {
debug!("Reading url: {}", file);
let client = reqwest::blocking::Client::new();
let resp = client
.get(file)
.header(CONTENT_TYPE, "application/x-turtle")
.send()?;
if !resp.status().is_success() {
return Err(anyhow::anyhow!("Failed to fetch ontology from {}", file));
}
let content_type = resp.headers().get("Content-Type");
let content_type = content_type.and_then(|ct| ct.to_str().ok());
let content_type = content_type.and_then(|ext| match ext {
"application/x-turtle" => Some(GraphFormat::Turtle),
"application/rdf+xml" => Some(GraphFormat::RdfXml),
"text/rdf+n3" => Some(GraphFormat::NTriples),
_ => {
debug!("Unknown content type: {}", ext);
None
}
});
let content: BufReader<_> = BufReader::new(std::io::Cursor::new(resp.bytes()?));
if let Some(format) = content_type {
let parser = GraphParser::from_format(format);
let mut graph = OxigraphGraph::new();
let triples = parser.read_triples(content)?;
for triple in triples {
graph.insert(&triple?);
}
return Ok(graph);
}
let content_vec: Vec<u8> = content.bytes().map(|b| b.unwrap()).collect();
for format in [
GraphFormat::Turtle,
GraphFormat::RdfXml,
GraphFormat::NTriples,
] {
let vcontent = BufReader::new(std::io::Cursor::new(&content_vec));
let parser = GraphParser::from_format(format);
let mut graph = OxigraphGraph::new();
let triples = parser.read_triples(vcontent);
if triples.is_err() {
continue;
}
for triple in triples.unwrap() {
graph.insert(&triple?);
}
return Ok(graph);
}
Err(anyhow::anyhow!("Failed to parse graph from {}", file))
}
#[cfg(test)]
mod tests {
use super::*;
use oxigraph::model::{Dataset, GraphNameRef, NamedNodeRef, QuadRef};
#[test]
fn test_read_file() {
let graph = read_file(Path::new("tests/data/model.ttl")).unwrap();
assert_eq!(graph.len(), 5);
let graph = read_file(Path::new("tests/data/model.nt")).unwrap();
assert_eq!(graph.len(), 5);
let graph = read_file(Path::new("tests/data/model.n3")).unwrap();
assert_eq!(graph.len(), 5);
let graph = read_file(Path::new("tests/data/model.xml")).unwrap();
assert_eq!(graph.len(), 5);
let graph = read_file(Path::new("tests/data/model")).unwrap();
assert_eq!(graph.len(), 5);
let result = read_file(Path::new("tests/data/non-existent.ttl"));
assert!(result.is_err());
}
#[test]
fn test_read_url() {
let graph =
read_url("https://github.com/BrickSchema/Brick/releases/download/v1.4.0-rc1/Brick.ttl")
.unwrap();
assert_eq!(graph.len(), 53478);
let result = read_url("http://example.org/non-existent.ttl");
assert!(result.is_err());
}
#[test]
fn test_write_dataset_to_file() {
let mut graph = Dataset::new();
let model = read_file(Path::new("tests/data/model.ttl")).unwrap();
let model_name =
GraphNameRef::NamedNode(NamedNodeRef::new("http://example.org/model").unwrap());
let brick = read_file(Path::new("tests/data/Brick-1.3.ttl")).unwrap();
let brick_name =
GraphNameRef::NamedNode(NamedNodeRef::new("http://example.org/brick").unwrap());
for quad in model.iter() {
graph.insert(QuadRef::new(
quad.subject,
quad.predicate,
quad.object,
model_name,
));
}
for quad in brick.iter() {
graph.insert(QuadRef::new(
quad.subject,
quad.predicate,
quad.object,
brick_name,
));
}
write_dataset_to_file(&graph, "model_out.ttl").unwrap();
}
}