From 36a51b554e0bb6b8c0d2ff57b131b6d189ef3114 Mon Sep 17 00:00:00 2001 From: Simon Johnston Date: Mon, 7 Oct 2024 18:11:16 -0700 Subject: [PATCH] refactor: simplify and move to core v0.5 --- rdftk_io/Cargo.toml | 8 +- rdftk_io/README.md | 5 + rdftk_io/src/common/indenter.rs | 73 +- rdftk_io/src/common/parser/mod.rs | 21 +- rdftk_io/src/common/parser/nquads.rs | 54 +- rdftk_io/src/common/parser/ntriples.rs | 82 +-- rdftk_io/src/dot/mod.rs | 4 +- rdftk_io/src/dot/writer.rs | 26 +- rdftk_io/src/json/mod.rs | 6 +- rdftk_io/src/json/reader.rs | 118 ++- rdftk_io/src/json/writer.rs | 12 +- rdftk_io/src/n3/mod.rs | 2 +- rdftk_io/src/n3/reader.rs | 21 +- rdftk_io/src/nq/mod.rs | 9 +- rdftk_io/src/nq/reader.rs | 24 +- rdftk_io/src/nq/writer.rs | 14 +- rdftk_io/src/nt/mod.rs | 6 +- rdftk_io/src/nt/reader.rs | 21 +- rdftk_io/src/nt/writer.rs | 7 +- rdftk_io/src/trig/mod.rs | 2 +- rdftk_io/src/trig/reader.rs | 21 +- rdftk_io/src/trig/writer.rs | 25 +- rdftk_io/src/turtle/mod.rs | 20 +- rdftk_io/src/turtle/reader.rs | 21 +- rdftk_io/src/turtle/writer.rs | 765 ++++++++++++++++++++ rdftk_io/src/turtle/writer/cursor.rs | 586 --------------- rdftk_io/src/turtle/writer/mod.rs | 13 - rdftk_io/src/turtle/writer/options.rs | 157 ---- rdftk_io/src/turtle/writer/triple_type.rs | 55 -- rdftk_io/src/turtle/writer/turtle_writer.rs | 33 - rdftk_io/src/xml/mod.rs | 5 +- rdftk_io/src/xml/reader.rs | 177 ++--- rdftk_io/src/xml/writer.rs | 38 +- rdftk_io/tests/common/mod.rs | 308 +++----- rdftk_io/tests/read_json.rs | 6 +- rdftk_io/tests/read_nt.rs | 8 +- rdftk_io/tests/read_turtle.rs | 4 +- rdftk_io/tests/read_xml.rs | 6 +- rdftk_io/tests/w3c_nt.rs | 4 +- rdftk_io/tests/write_nq.rs | 11 +- rdftk_io/tests/write_turtle.rs | 15 +- 41 files changed, 1230 insertions(+), 1563 deletions(-) create mode 100644 rdftk_io/src/turtle/writer.rs delete mode 100644 rdftk_io/src/turtle/writer/cursor.rs delete mode 100644 rdftk_io/src/turtle/writer/mod.rs delete mode 100644 rdftk_io/src/turtle/writer/options.rs delete mode 100644 rdftk_io/src/turtle/writer/triple_type.rs delete mode 100644 rdftk_io/src/turtle/writer/turtle_writer.rs diff --git a/rdftk_io/Cargo.toml b/rdftk_io/Cargo.toml index d872718..908a008 100644 --- a/rdftk_io/Cargo.toml +++ b/rdftk_io/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rdftk_io" -version = "0.3.0" +version = "0.3.1" authors = ["Simon Johnston "] edition = "2021" description = "This crate provides traits for reading and writing Statements and Graphs as well as implementations of these for common representations." @@ -29,15 +29,15 @@ xml = ["xml-rs", "rdftk_names"] itertools = "0.13" lazy_static = "1.4" objio = "0.1.1" -rdftk_core = { version = "0.4.2", path = "../rdftk_core" } -rdftk_iri = { version = "0.2.2", path = "../rdftk_iri" } +rdftk_core = { version = "0.5.0", path = "../rdftk_core" } +rdftk_iri = { version = "0.2.5", path = "../rdftk_iri" } regex = "1.5" tracing = "0.1.40" # feature-dependencies pest = { version = "2.7", optional = true } pest_derive = { version = "2.7", optional = true } -rdftk_names = { version = "0.2.1", path = "../rdftk_names", optional = true } +rdftk_names = { version = "0.2.3", path = "../rdftk_names", optional = true } serde_json = { version = "1.0", optional = true } xml-rs = { version = "0.8", optional = true } diff --git a/rdftk_io/README.md b/rdftk_io/README.md index 99f679a..d2c890f 100644 --- a/rdftk_io/README.md +++ b/rdftk_io/README.md @@ -46,6 +46,11 @@ let graph = reader.read(&mut file, graph_factory()).unwrap(); ## Changes +### Version 0.3.1 + +* Feature: moved to new v0.5 core package. +* Tests: all tests now passing. + ### Version 0.3.0 * Feature: moved to new `rdftk_core` package. diff --git a/rdftk_io/src/common/indenter.rs b/rdftk_io/src/common/indenter.rs index f66bb30..bf56195 100644 --- a/rdftk_io/src/common/indenter.rs +++ b/rdftk_io/src/common/indenter.rs @@ -1,3 +1,4 @@ +use std::cell::RefCell; use std::fmt::{Display, Formatter}; // ------------------------------------------------------------------------------------------------ @@ -7,7 +8,7 @@ use std::fmt::{Display, Formatter}; #[derive(Clone, Debug)] pub(crate) struct Indenter { width: usize, - depth: usize, + depth: RefCell, } // ------------------------------------------------------------------------------------------------ @@ -16,56 +17,74 @@ pub(crate) struct Indenter { impl Default for Indenter { fn default() -> Self { - Self { width: 2, depth: 0 } + Self { + width: 2, + depth: RefCell::new(0), + } } } impl Display for Indenter { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{:width$}", "", width = self.width * self.depth) + write!( + f, + "{:width$}", + "", + width = self.width * (*self.depth.borrow()) + ) } } impl Indenter { - pub(crate) fn with_width(self, width: usize) -> Self { - Self { width, ..self } + pub(crate) fn with_default_indent_width(self, width: usize) -> Self { + let mut self_mut = self; + self_mut.width = width; + self_mut } - pub(crate) fn with_depth(self, depth: usize) -> Self { - Self { depth, ..self } + #[allow(dead_code)] + pub(crate) fn with_initial_depth(self, depth: usize) -> Self { + let _ = self.depth.replace(depth); + self } pub(crate) fn depth(&self) -> usize { - self.depth + *self.depth.borrow() } - pub(crate) fn reset_depth(&mut self) { - self.depth = 0 + #[allow(dead_code)] + pub(crate) fn default_width(&self) -> usize { + self.width } - pub(crate) fn indent(&self) -> Self { - self.indent_by(1) + pub(crate) fn reset_depth(&self) { + self.depth.replace(0); } - pub(crate) fn indent_by(&self, by: usize) -> Self { - Self { - width: self.width, - depth: self.depth + by, - } + pub(crate) fn indent(&self) { + self.indent_by(self.width); } - pub(crate) fn outdent(&self) -> Self { - self.outdent_by(1) + pub(crate) fn indent_by(&self, by: usize) { + self.depth.replace(self.depth() + by); } - pub(crate) fn outdent_by(&self, by: usize) -> Self { - Self { - width: self.width, - depth: self.depth - by, - } + #[allow(dead_code)] + pub(crate) fn indent_for>(&self, for_: T) { + self.indent_by(for_.into()) } - //pub(crate) fn one(&self) -> String { - // format!("{:width$}", "", width = self.width) - //} + pub(crate) fn outdent(&self) { + self.indent_by(self.width); + } + + #[allow(dead_code)] + pub(crate) fn outdent_by(&self, by: usize) { + self.depth.replace(self.depth() - by); + } + + #[allow(dead_code)] + pub(crate) fn outdent_for>(&self, for_: T) { + self.outdent_by(for_.into()) + } } diff --git a/rdftk_io/src/common/parser/mod.rs b/rdftk_io/src/common/parser/mod.rs index 7830caf..5364207 100644 --- a/rdftk_io/src/common/parser/mod.rs +++ b/rdftk_io/src/common/parser/mod.rs @@ -3,8 +3,8 @@ use pest::Parser as _; use pest_derive::Parser; use rdftk_core::error::Error; -use rdftk_core::model::data_set::{DataSetFactoryRef, DataSetRef}; -use rdftk_core::model::graph::{GraphFactoryRef, GraphRef}; +use rdftk_core::model::data_set::DataSet; +use rdftk_core::model::graph::Graph; use tracing::{span, Level}; // ------------------------------------------------------------------------------------------------ @@ -66,7 +66,7 @@ macro_rules! pest_error { // Public Functions // ------------------------------------------------------------------------------------------------ -pub(crate) fn parse_ntriple_doc(source: S, factory: GraphFactoryRef) -> Result +pub(crate) fn parse_ntriple_doc(source: S) -> Result where S: AsRef, { @@ -78,10 +78,10 @@ where source: Box::new(e), })?; let top_node = parsed.next().unwrap(); - ntriples::parse_doc(top_node, factory) + ntriples::parse_doc(top_node) } -pub(crate) fn parse_nquad_doc(source: S, factory: DataSetFactoryRef) -> Result +pub(crate) fn parse_nquad_doc(source: S) -> Result where S: AsRef, { @@ -93,10 +93,10 @@ where source: Box::new(e), })?; let top_node = parsed.next().unwrap(); - nquads::parse_doc(top_node, factory) + nquads::parse_doc(top_node) } -pub(crate) fn parse_turtle_doc(_source: S, _factory: GraphFactoryRef) -> Result +pub(crate) fn parse_turtle_doc(_source: S) -> Result where S: AsRef, { @@ -105,10 +105,7 @@ where todo!() } -pub(crate) fn parse_trig_doc( - _source: S, - _factory: DataSetFactoryRef, -) -> Result +pub(crate) fn parse_trig_doc(_source: S) -> Result where S: AsRef, { @@ -117,7 +114,7 @@ where todo!() } -pub(crate) fn parse_n3_doc(_source: S, _factory: GraphFactoryRef) -> Result +pub(crate) fn parse_n3_doc(_source: S) -> Result where S: AsRef, { diff --git a/rdftk_io/src/common/parser/nquads.rs b/rdftk_io/src/common/parser/nquads.rs index b061b6c..09b39c2 100644 --- a/rdftk_io/src/common/parser/nquads.rs +++ b/rdftk_io/src/common/parser/nquads.rs @@ -1,34 +1,26 @@ use super::ntriples::{object as nt_object, predicate as nt_predicate, subject as nt_subject}; use super::Rule; use pest::iterators::Pair; +use rdftk_core::error::Error; use rdftk_core::model::data_set::DataSet; -use rdftk_core::model::graph::named::{GraphName, GraphNameRef}; -use rdftk_core::model::graph::NamedGraphRef; -use rdftk_core::model::statement::{ObjectNodeRef, StatementRef, SubjectNodeRef}; -use rdftk_core::{ - error::Error, - model::data_set::{DataSetFactoryRef, DataSetRef}, -}; -use rdftk_iri::IriRef; -use std::cell::RefMut; +use rdftk_core::model::graph::{Graph, GraphName}; +use rdftk_core::model::statement::{ObjectNode, Statement, SubjectNode}; +use rdftk_iri::Iri; // ------------------------------------------------------------------------------------------------ // Public Functions // ------------------------------------------------------------------------------------------------ -pub(super) fn parse_doc( - input_pair: Pair<'_, Rule>, - factory: DataSetFactoryRef, -) -> Result { +pub(super) fn parse_doc(input_pair: Pair<'_, Rule>) -> Result { parse_rule!("nquadDoc" entry input_pair); - let data_set = factory.data_set(); + let mut data_set = DataSet::default(); if input_pair.as_rule() == Rule::nquadDoc { for inner_pair in input_pair.into_inner() { match inner_pair.as_rule() { Rule::nquad => { - nquad(inner_pair, data_set.borrow_mut())?; + nquad(inner_pair, &mut data_set)?; } Rule::EOI => {} _ => { @@ -51,42 +43,34 @@ pub(super) fn parse_doc( // Private Functions // ------------------------------------------------------------------------------------------------ -fn subject_to_name(subject: SubjectNodeRef) -> GraphNameRef { - let name: GraphName = subject.into(); - name.into() +fn subject_to_name(subject: SubjectNode) -> GraphName { + subject.into() } -fn nquad(input_pair: Pair<'_, Rule>, data_set: RefMut<'_, dyn DataSet>) -> Result<(), Error> { +fn nquad(input_pair: Pair<'_, Rule>, data_set: &mut DataSet) -> Result<(), Error> { parse_rule!("nquad" entry input_pair); - let mut data_set = data_set; - - let graphs = data_set.graph_factory(); - let statements = graphs.statement_factory(); - let literals = statements.literal_factory(); - if input_pair.as_rule() == Rule::nquad { let mut inner_pairs = input_pair.into_inner(); - let subject: SubjectNodeRef = nt_subject(inner_pairs.next().unwrap(), &statements)?; - let predicate: IriRef = nt_predicate(inner_pairs.next().unwrap())?; - let object: ObjectNodeRef = nt_object(inner_pairs.next().unwrap(), &statements, &literals)?; - let statement: StatementRef = statements.statement(subject, predicate, object)?; - let graph: &mut NamedGraphRef = if let Some(new_inner_pair) = inner_pairs.next() { - let graph_name = subject_to_name(nt_subject(new_inner_pair, &statements)?); + let subject: SubjectNode = nt_subject(inner_pairs.next().unwrap())?; + let predicate: Iri = nt_predicate(inner_pairs.next().unwrap())?; + let object: ObjectNode = nt_object(inner_pairs.next().unwrap())?; + let statement: Statement = Statement::new(subject, predicate, object); + let graph: &mut Graph = if let Some(new_inner_pair) = inner_pairs.next() { + let graph_name = subject_to_name(nt_subject(new_inner_pair)?); if let Some(graph) = data_set.graph_mut(&Some(graph_name.clone())) { graph } else { - data_set.insert(graphs.named_graph(Some(graph_name.clone()))); + data_set.insert(Graph::named(graph_name.clone())); data_set.graph_mut(&Some(graph_name)).unwrap() } } else if let Some(graph) = data_set.graph_mut(&None) { graph } else { - data_set.insert(graphs.named_graph(None)); + data_set.insert(Graph::default()); data_set.graph_mut(&None).unwrap() }; - let mut graph_mut = graph.borrow_mut(); - graph_mut.insert(statement); + graph.insert(statement); Ok(()) } else { Err(pest_error!(unexpected RULE_FN, &input_pair, [Rule::nquad])) diff --git a/rdftk_io/src/common/parser/ntriples.rs b/rdftk_io/src/common/parser/ntriples.rs index 685a633..d54adf0 100644 --- a/rdftk_io/src/common/parser/ntriples.rs +++ b/rdftk_io/src/common/parser/ntriples.rs @@ -1,12 +1,10 @@ use super::Rule; use pest::iterators::Pair; use rdftk_core::error::Error; -use rdftk_core::model::graph::{GraphFactoryRef, GraphRef}; -use rdftk_core::model::literal::{DataType, LanguageTag, LiteralFactoryRef, LiteralRef}; -use rdftk_core::model::statement::{ - ObjectNodeRef, StatementFactoryRef, StatementRef, SubjectNodeRef, -}; -use rdftk_iri::{Iri, IriRef}; +use rdftk_core::model::graph::Graph; +use rdftk_core::model::literal::{DataType, LanguageTag, Literal}; +use rdftk_core::model::statement::{BlankNode, ObjectNode, Statement, SubjectNode}; +use rdftk_iri::Iri; use regex::Regex; use std::str::FromStr; @@ -14,24 +12,16 @@ use std::str::FromStr; // Public Functions // ------------------------------------------------------------------------------------------------ -pub(super) fn parse_doc( - input_pair: Pair<'_, Rule>, - factory: GraphFactoryRef, -) -> Result { +pub(super) fn parse_doc(input_pair: Pair<'_, Rule>) -> Result { parse_rule!("parse_doc" entry input_pair); - let graph = factory.graph(); + let mut graph = Graph::default(); if input_pair.as_rule() == Rule::ntripleDoc { for inner_pair in input_pair.into_inner() { match inner_pair.as_rule() { Rule::ntriple => { - let mut graph = graph.borrow_mut(); - let st = triple( - inner_pair, - &graph.statement_factory(), - &graph.literal_factory(), - )?; + let st = triple(inner_pair)?; graph.insert(st); } Rule::EOI => { @@ -54,39 +44,32 @@ pub(super) fn parse_doc( unreachable!() } -fn triple( - input_pair: Pair<'_, Rule>, - statements: &StatementFactoryRef, - literals: &LiteralFactoryRef, -) -> Result { +fn triple(input_pair: Pair<'_, Rule>) -> Result { parse_rule!("triple" entry input_pair); if input_pair.as_rule() == Rule::ntriple { let mut inner_pairs = input_pair.into_inner(); - let subject = subject(inner_pairs.next().unwrap(), statements)?; + let subject = subject(inner_pairs.next().unwrap())?; let predicate = predicate(inner_pairs.next().unwrap())?; - let object = object(inner_pairs.next().unwrap(), statements, literals)?; - statements.statement(subject, predicate, object) + let object = object(inner_pairs.next().unwrap())?; + Ok(Statement::new(subject, predicate, object)) } else { Err(pest_error!(unexpected RULE_FN, &input_pair, [Rule::ntriple])) } } -pub(crate) fn subject( - input_pair: Pair<'_, Rule>, - factory: &StatementFactoryRef, -) -> Result { +pub(crate) fn subject(input_pair: Pair<'_, Rule>) -> Result { parse_rule!("nt_subject" entry input_pair); if input_pair.as_rule() == Rule::ntripleSubject { let inner_pair = input_pair.into_inner().next().unwrap(); match inner_pair.as_rule() { - Rule::IRIREF => Ok(factory.named_subject(iri_ref(inner_pair)?)), + Rule::IRIREF => Ok(iri_ref(inner_pair)?.into()), Rule::blankNode => { let node = inner_pair.as_str().to_string(); // strip the leading '_:' let node = &node[2..]; - factory.blank_subject_named(node) + Ok(BlankNode::from_str(node)?.into()) } _ => Err(pest_error!( unexpected @@ -100,7 +83,7 @@ pub(crate) fn subject( } } -pub(crate) fn predicate(input_pair: Pair<'_, Rule>) -> Result { +pub(crate) fn predicate(input_pair: Pair<'_, Rule>) -> Result { parse_rule!("predicate" entry input_pair); if input_pair.as_rule() == Rule::ntriplePredicate { @@ -115,26 +98,22 @@ pub(crate) fn predicate(input_pair: Pair<'_, Rule>) -> Result { } } -pub(crate) fn object( - input_pair: Pair<'_, Rule>, - factory: &StatementFactoryRef, - literals: &LiteralFactoryRef, -) -> Result { +pub(crate) fn object(input_pair: Pair<'_, Rule>) -> Result { parse_rule!("object" entry input_pair); if input_pair.as_rule() == Rule::ntripleObject { let inner_pair = input_pair.into_inner().next().unwrap(); match inner_pair.as_rule() { - Rule::IRIREF => Ok(factory.named_object(iri_ref(inner_pair)?)), + Rule::IRIREF => Ok(iri_ref(inner_pair)?.into()), Rule::blankNode => { let node = inner_pair.as_str().to_string(); // strip the leading '_:' let node = &node[2..]; - Ok(factory.blank_object_named(node)?) + Ok(BlankNode::from_str(node)?.into()) } Rule::ntripleLiteral => { - let literal = literal(inner_pair, literals)?; - Ok(factory.literal_object(literal)) + let literal = literal(inner_pair)?; + Ok(literal.into()) } _ => Err(pest_error!( unexpected @@ -148,21 +127,18 @@ pub(crate) fn object( } } -fn literal(input_pair: Pair<'_, Rule>, literals: &LiteralFactoryRef) -> Result { +fn literal(input_pair: Pair<'_, Rule>) -> Result { parse_rule!("literal" entry input_pair); if input_pair.as_rule() == Rule::ntripleLiteral { let inner_pair = input_pair.into_inner().next().unwrap(); - rdf_literal(inner_pair, literals) + rdf_literal(inner_pair) } else { Err(pest_error!(unexpected RULE_FN, &input_pair, [Rule::ntripleObject])) } } -fn rdf_literal( - input_pair: Pair<'_, Rule>, - literals: &LiteralFactoryRef, -) -> Result { +fn rdf_literal(input_pair: Pair<'_, Rule>) -> Result { parse_rule!("rdf_literal" entry input_pair); if input_pair.as_rule() == Rule::ntripleRdfLiteral { @@ -173,11 +149,11 @@ fn rdf_literal( match other.as_rule() { Rule::IRIREF => { let data_type = DataType::Other(iri_ref(other)?); - Ok(literals.with_data_type(&lexical_form, data_type)) + Ok(Literal::with_data_type(&lexical_form, data_type)) } Rule::LANGTAG => { let lang_tag = lang_tag(other)?; - Ok(literals.with_language(&lexical_form, lang_tag)) + Ok(Literal::with_language(&lexical_form, lang_tag)) } _ => Err(pest_error!( unexpected @@ -187,7 +163,7 @@ fn rdf_literal( )), } } else { - Ok(literals.literal(&lexical_form)) + Ok(Literal::plain(&lexical_form)) } } else { Err(pest_error!(unexpected RULE_FN, &input_pair, [Rule::ntripleRdfLiteral])) @@ -225,14 +201,14 @@ fn string(input_pair: Pair<'_, Rule>) -> Result { } } -fn iri_ref(input_pair: Pair<'_, Rule>) -> Result { +fn iri_ref(input_pair: Pair<'_, Rule>) -> Result { parse_rule!("iri_ref" entry input_pair); if input_pair.as_rule() == Rule::IRIREF { let iri = input_pair.as_str().to_string(); // strip the '<' and '>' characters. let iri_str = unescape_iri(&iri[1..iri.len() - 1]); - Ok(IriRef::new(Iri::from_str(&iri_str)?)) + Ok(Iri::from_str(&iri_str)?) } else { Err(pest_error!(unexpected RULE_FN, &input_pair, [Rule::IRIREF])) } @@ -247,7 +223,7 @@ fn lang_tag(input_pair: Pair<'_, Rule>) -> Result { // strip the leading '@' let tag = &tag[1..]; println!("**{tag}**"); - Ok(LanguageTag::from_str(tag)?) + Ok(LanguageTag::parse(tag)?) } else { Err(pest_error!(unexpected RULE_FN, &input_pair, [Rule::LANGTAG])) } diff --git a/rdftk_io/src/dot/mod.rs b/rdftk_io/src/dot/mod.rs index 85890c0..30afbbe 100644 --- a/rdftk_io/src/dot/mod.rs +++ b/rdftk_io/src/dot/mod.rs @@ -8,8 +8,8 @@ format. ```rust use rdftk_io::dot::{DotOptions, DotWriter}; # use objio::{HasOptions, ObjectWriter}; -# use rdftk_core::model::graph::GraphRef; -# fn make_graph() -> GraphRef { rdftk_core::simple::graph::graph_factory().graph() } +# use rdftk_core::model::graph::Graph; +# fn make_graph() -> Graph { rdftk_core::model::graph::Graph::default() } let mut options = DotOptions::default().with_blank_labels(true); diff --git a/rdftk_io/src/dot/writer.rs b/rdftk_io/src/dot/writer.rs index e8ec5d1..09cbcdd 100644 --- a/rdftk_io/src/dot/writer.rs +++ b/rdftk_io/src/dot/writer.rs @@ -1,7 +1,7 @@ use objio::{impl_has_options, ObjectWriter}; use rdftk_core::error::Error; -use rdftk_core::model::graph::GraphRef; -use rdftk_core::model::statement::{ObjectNodeRef, SubjectNodeRef}; +use rdftk_core::model::graph::Graph; +use rdftk_core::model::statement::{ObjectNode, SubjectNode}; use std::cell::RefCell; use std::collections::HashMap; use std::io::Write; @@ -264,18 +264,18 @@ impl DotOptions { impl_has_options!(DotWriter, DotOptions); -impl ObjectWriter for DotWriter { +impl ObjectWriter for DotWriter { type Error = Error; - fn write(&self, w: &mut W, object: &GraphRef) -> Result<(), Self::Error> + fn write(&self, w: &mut W, graph: &Graph) -> Result<(), Self::Error> where W: Write, { writeln!(w, "digraph {{\n rankdir=BT\n charset=\"utf-8\";")?; - writeln!(w)?; + // TODO: emit graph name - let graph = object.borrow(); + writeln!(w)?; let mappings = graph.prefix_mappings(); for statement in graph.statements() { @@ -285,7 +285,7 @@ impl ObjectWriter for DotWriter { self.options.node_prefix, self.subject_id(statement.subject()), self.object_id(statement.object()), - match mappings.borrow().compress(statement.predicate()) { + match mappings.compress(statement.predicate()) { None => statement.predicate().to_string(), Some(qname) => qname.to_string(), } @@ -361,7 +361,7 @@ impl DotWriter { } } - fn subject_id(&self, node: &SubjectNodeRef) -> String { + fn subject_id(&self, node: &SubjectNode) -> String { let mut nodes = self.nodes.borrow_mut(); if let Some(node) = nodes.get(&node.to_string()) { node.id.clone() @@ -376,13 +376,13 @@ impl DotWriter { label: node.as_blank().unwrap().as_ref().into(), }, ); - } else if node.is_iri() { + } else if node.is_resource() { let _ = nodes.insert( node.to_string(), Node { id: id.clone(), kind: NodeKind::Iri, - label: node.as_iri().unwrap().to_string(), + label: node.as_resource().unwrap().to_string(), }, ); } @@ -390,7 +390,7 @@ impl DotWriter { } } - fn object_id(&self, node: &ObjectNodeRef) -> String { + fn object_id(&self, node: &ObjectNode) -> String { let mut nodes = self.nodes.borrow_mut(); if let Some(node) = nodes.get(&node.to_string()) { node.id.clone() @@ -405,13 +405,13 @@ impl DotWriter { label: node.as_blank().unwrap().as_ref().into(), }, ); - } else if node.is_iri() { + } else if node.is_resource() { let _ = nodes.insert( node.to_string(), Node { id: id.clone(), kind: NodeKind::Iri, - label: node.as_iri().unwrap().to_string(), + label: node.as_resource().unwrap().to_string(), }, ); } else if node.is_literal() { diff --git a/rdftk_io/src/json/mod.rs b/rdftk_io/src/json/mod.rs index 860f09c..886399c 100644 --- a/rdftk_io/src/json/mod.rs +++ b/rdftk_io/src/json/mod.rs @@ -7,8 +7,8 @@ W3C [RDF 1.1 JSON Alternate Serialization (RDF/JSON)](https://www.w3.org/TR/rdf- ```rust use rdftk_io::json::{JsonWriter, JsonOptions}; # use objio::{HasOptions, ObjectWriter}; -# use rdftk_core::model::graph::GraphRef; -# fn make_graph() -> GraphRef { rdftk_core::simple::graph::graph_factory().graph() } +# use rdftk_core::model::graph::Graph; +# fn make_graph() -> Graph { Graph::default() } let writer = JsonWriter::default() .with_options(JsonOptions::default().with_pretty_print(true)); @@ -38,7 +38,7 @@ pub const MIME_TYPE: &str = "application/rdf+json"; mod syntax; mod reader; -pub use reader::{JsonReader, JsonReaderOptions}; +pub use reader::JsonReader; mod writer; pub use writer::{JsonOptions, JsonWriter}; diff --git a/rdftk_io/src/json/reader.rs b/rdftk_io/src/json/reader.rs index f214e14..4d468be 100644 --- a/rdftk_io/src/json/reader.rs +++ b/rdftk_io/src/json/reader.rs @@ -1,52 +1,42 @@ +use super::NAME; use crate::json::syntax::{ BNODE_PREFIX, OBJ_KEY_DATATYPE, OBJ_KEY_LANG, OBJ_KEY_TYPE, OBJ_KEY_VALUE, OBJ_TYPE_BNODE, OBJ_TYPE_LITERAL, OBJ_TYPE_URI, }; -use crate::make_factory_options; -use objio::{impl_has_options, HasOptions, ObjectReader}; +use objio::ObjectReader; use rdftk_core::error::Error; -use rdftk_core::model::graph::{GraphFactoryRef, GraphRef}; -use rdftk_core::model::literal::{DataType, LanguageTag}; -use rdftk_core::model::statement::SubjectNodeRef; -use rdftk_core::simple::graph_factory; -use rdftk_core::simple::statement::statement_factory; -use rdftk_iri::{Iri, IriRef}; +use rdftk_core::model::graph::Graph; +use rdftk_core::model::literal::{DataType, LanguageTag, Literal}; +use rdftk_core::model::statement::{BlankNode, Statement, SubjectNode}; +use rdftk_iri::Iri; use serde_json::{Map, Value}; use std::io::Read; use std::str::FromStr; use tracing::error; -use super::NAME; - // ------------------------------------------------------------------------------------------------ // Public Types // ------------------------------------------------------------------------------------------------ -make_factory_options!(JsonReaderOptions, GraphFactoryRef, graph_factory); - /// /// An implementation of the GraphReader trait to read resources in the JSON representation. /// #[derive(Debug, Default)] -pub struct JsonReader { - options: JsonReaderOptions, -} +pub struct JsonReader {} // ------------------------------------------------------------------------------------------------ // Implementations // ------------------------------------------------------------------------------------------------ -impl_has_options!(JsonReader, JsonReaderOptions); - -impl ObjectReader for JsonReader { +impl ObjectReader for JsonReader { type Error = Error; - fn read(&self, r: &mut R) -> Result + fn read(&self, r: &mut R) -> Result where R: Read, { let value: Value = serde_json::from_reader(r).map_err(json_error)?; - parse_graph(value, self.options().factory().clone()) + parse_graph(value) } } @@ -80,13 +70,13 @@ fn value_variant(value: &Value) -> String { .to_string() } -fn parse_graph(value: Value, factory: GraphFactoryRef) -> Result { +fn parse_graph(value: Value) -> Result { parse_rule!("parse_graph" entry); if let Value::Object(json) = value { - let graph = factory.graph(); + let mut graph = Graph::default(); for (subject, predicate_objects) in json.iter() { - parse_statements(subject, predicate_objects, &graph)?; + parse_statements(subject, predicate_objects, &mut graph)?; } Ok(graph) } else { @@ -102,21 +92,15 @@ fn parse_graph(value: Value, factory: GraphFactoryRef) -> Result Result<(), Error> { parse_rule!("parse_statements" entry); if let Value::Object(json) = predicate_objects { let subject = if let Some(subject) = subject.strip_prefix(BNODE_PREFIX) { - graph - .borrow() - .statement_factory() - .blank_subject_named(subject)? + BlankNode::from_str(subject)?.into() } else { - graph - .borrow() - .statement_factory() - .named_subject(IriRef::new(Iri::from_str(subject)?)) + Iri::from_str(subject)?.into() }; for (predicate, objects) in json.iter() { parse_predicates(&subject, predicate, objects, graph)?; @@ -133,15 +117,15 @@ fn parse_statements( } fn parse_predicates( - subject: &SubjectNodeRef, + subject: &SubjectNode, predicate: &str, objects: &Value, - graph: &GraphRef, + graph: &mut Graph, ) -> Result<(), Error> { parse_rule!("parse_predicates" entry); if let Value::Array(json) = objects { - let predicate = IriRef::new(Iri::from_str(predicate)?); + let predicate = Iri::from_str(predicate)?; for object in json { parse_object(subject, &predicate, object, graph)?; } @@ -157,10 +141,10 @@ fn parse_predicates( } fn parse_object( - subject: &SubjectNodeRef, - predicate: &IriRef, + subject: &SubjectNode, + predicate: &Iri, object: &Value, - graph: &GraphRef, + graph: &mut Graph, ) -> Result<(), Error> { parse_rule!("parse_object" entry); @@ -208,28 +192,24 @@ fn parse_object( } fn parse_literal_object( - subject: &SubjectNodeRef, - predicate: &IriRef, + subject: &SubjectNode, + predicate: &Iri, object: &Map, - graph: &GraphRef, + graph: &mut Graph, ) -> Result<(), Error> { parse_rule!("parse_literal_object" entry); - - let mut graph = graph.borrow_mut(); let value = object.get(OBJ_KEY_VALUE); let language = object.get(OBJ_KEY_LANG); let data_type = object.get(OBJ_KEY_DATATYPE); - let object = statement_factory().literal_object(match (value, language, data_type) { - (Some(Value::String(v)), None, None) => graph.literal_factory().literal(v), - (Some(Value::String(v)), Some(Value::String(l)), None) => graph - .literal_factory() - .with_language(v, LanguageTag::from_str(l)?), + let object = match (value, language, data_type) { + (Some(Value::String(v)), None, None) => Literal::plain(v), + (Some(Value::String(v)), Some(Value::String(l)), None) => { + Literal::with_language(v, LanguageTag::from_str(l)?) + } (Some(Value::String(v)), None, Some(Value::String(d))) => { - let data_type = IriRef::new(Iri::from_str(d)?); - graph - .literal_factory() - .with_data_type(v, DataType::from(data_type)) + let data_type = Iri::from_str(d)?; + Literal::with_data_type(v, DataType::from(data_type)) } _ => { error!("parse_literal_object() - bad value/data type/language combination"); @@ -238,28 +218,22 @@ fn parse_literal_object( given: "bad value/data type/language combination".into(), }); } - }); - let st = graph - .statement_factory() - .statement(subject.clone(), predicate.clone(), object)?; + }; + let st = Statement::new(subject.clone(), predicate.clone(), object); graph.insert(st); Ok(()) } fn parse_bnode_object( - subject: &SubjectNodeRef, - predicate: &IriRef, + subject: &SubjectNode, + predicate: &Iri, object: &Map, - graph: &GraphRef, + graph: &mut Graph, ) -> Result<(), Error> { parse_rule!("parse_bnode_object" entry); - - let mut graph = graph.borrow_mut(); if let Some(Value::String(s)) = object.get(OBJ_KEY_VALUE) { - let object = graph.statement_factory().blank_object_named(&s[2..])?; - let st = graph - .statement_factory() - .statement(subject.clone(), predicate.clone(), object)?; + let object = BlankNode::from_str(&s[2..])?; + let st = Statement::new(subject.clone(), predicate.clone(), object); graph.insert(st); Ok(()) } else { @@ -275,20 +249,16 @@ fn parse_bnode_object( } fn parse_uri_object( - subject: &SubjectNodeRef, - predicate: &IriRef, + subject: &SubjectNode, + predicate: &Iri, object: &Map, - graph: &GraphRef, + graph: &mut Graph, ) -> Result<(), Error> { parse_rule!("parse_uri_object" entry); - let mut graph = graph.borrow_mut(); if let Some(Value::String(s)) = object.get(OBJ_KEY_VALUE) { - let uri = IriRef::new(Iri::from_str(s)?); - let object = graph.statement_factory().named_object(uri); - let st = graph - .statement_factory() - .statement(subject.clone(), predicate.clone(), object)?; + let object = Iri::from_str(s)?; + let st = Statement::new(subject.clone(), predicate.clone(), object); graph.insert(st); Ok(()) } else { diff --git a/rdftk_io/src/json/writer.rs b/rdftk_io/src/json/writer.rs index b27134f..da704ef 100644 --- a/rdftk_io/src/json/writer.rs +++ b/rdftk_io/src/json/writer.rs @@ -5,7 +5,7 @@ use crate::json::syntax::{ use crate::json::NAME; use objio::{impl_has_options, ObjectWriter}; use rdftk_core::error::{rdf_star_not_supported_error, Error}; -use rdftk_core::model::graph::GraphRef; +use rdftk_core::model::graph::Graph; use serde_json::{Map, Value}; use std::io::Write; use tracing::error; @@ -49,15 +49,13 @@ impl JsonOptions { impl_has_options!(JsonWriter, JsonOptions); -impl ObjectWriter for JsonWriter { +impl ObjectWriter for JsonWriter { type Error = Error; - fn write(&self, w: &mut W, object: &GraphRef) -> Result<(), Self::Error> + fn write(&self, w: &mut W, graph: &Graph) -> Result<(), Self::Error> where W: Write, { - let graph = object.borrow(); - let mut json_graph = Map::new(); for subject in graph.subjects() { let mut predicate_map = Map::new(); @@ -74,14 +72,14 @@ impl ObjectWriter for JsonWriter { OBJ_KEY_VALUE.to_string(), Value::String(object.as_blank().unwrap().to_string()), ); - } else if object.is_iri() { + } else if object.is_resource() { let _ = object_map.insert( OBJ_KEY_TYPE.to_string(), Value::String(OBJ_TYPE_URI.to_string()), ); let _ = object_map.insert( OBJ_KEY_VALUE.to_string(), - Value::String(object.as_iri().unwrap().to_string()), + Value::String(object.as_resource().unwrap().to_string()), ); } else if object.is_literal() { let literal = object.as_literal().unwrap(); diff --git a/rdftk_io/src/n3/mod.rs b/rdftk_io/src/n3/mod.rs index 2fb12b1..5a267df 100644 --- a/rdftk_io/src/n3/mod.rs +++ b/rdftk_io/src/n3/mod.rs @@ -25,6 +25,6 @@ pub const FORMAT_IRI: &str = "http://www.w3.org/ns/formats/N3"; // ------------------------------------------------------------------------------------------------ mod reader; -pub use reader::{N3Reader, N3ReaderOptions}; +pub use reader::N3Reader; pub mod writer; diff --git a/rdftk_io/src/n3/reader.rs b/rdftk_io/src/n3/reader.rs index ca2c78f..9774286 100644 --- a/rdftk_io/src/n3/reader.rs +++ b/rdftk_io/src/n3/reader.rs @@ -1,38 +1,29 @@ use crate::common::parser::parse_n3_doc; -use crate::make_factory_options; -use objio::{impl_has_options, HasOptions, ObjectReader}; +use objio::ObjectReader; use rdftk_core::error::Error; -use rdftk_core::model::graph::{GraphFactoryRef, GraphRef}; -use rdftk_core::simple::graph_factory; +use rdftk_core::model::graph::Graph; use std::io::Read; // ------------------------------------------------------------------------------------------------ // Public Types // ------------------------------------------------------------------------------------------------ -make_factory_options!(N3ReaderOptions, GraphFactoryRef, graph_factory); - #[derive(Debug, Default)] -pub struct N3Reader { - options: N3ReaderOptions, -} +pub struct N3Reader {} // ------------------------------------------------------------------------------------------------ // Implementations // ------------------------------------------------------------------------------------------------ -impl_has_options!(N3Reader, N3ReaderOptions); - -impl ObjectReader for N3Reader { +impl ObjectReader for N3Reader { type Error = Error; - fn read(&self, r: &mut R) -> Result + fn read(&self, r: &mut R) -> Result where R: Read, { let mut buffer = String::new(); r.read_to_string(&mut buffer)?; - let factory = self.options().factory().clone(); - parse_n3_doc(buffer, factory) + parse_n3_doc(buffer) } } diff --git a/rdftk_io/src/nq/mod.rs b/rdftk_io/src/nq/mod.rs index 7d62a72..54ee371 100644 --- a/rdftk_io/src/nq/mod.rs +++ b/rdftk_io/src/nq/mod.rs @@ -11,11 +11,8 @@ Provides the `NQuadDataSetWriter` implementation of the `DataSetWriter` trait an ```rust use rdftk_io::nq::NQuadWriter; # use objio::ObjectWriter; -# use std::cell::RefCell; -# use std::rc::Rc; -# use rdftk_core::model::data_set::DataSetRef; -# use rdftk_core::simple::data_set::data_set_factory; -# fn make_data_set() -> DataSetRef { data_set_factory().data_set() } +# use rdftk_core::model::data_set::DataSet; +# fn make_data_set() -> DataSet { DataSet::default() } let writer = NQuadWriter::default(); @@ -45,7 +42,7 @@ pub const FORMAT_IRI: &str = "http://www.w3.org/ns/formats/N-Quads"; // ------------------------------------------------------------------------------------------------ mod reader; -pub use reader::{NQuadReader, NQuadReaderOptions}; +pub use reader::NQuadReader; mod writer; pub use writer::NQuadWriter; diff --git a/rdftk_io/src/nq/reader.rs b/rdftk_io/src/nq/reader.rs index b64988d..939d42e 100644 --- a/rdftk_io/src/nq/reader.rs +++ b/rdftk_io/src/nq/reader.rs @@ -1,40 +1,28 @@ use crate::common::parser::parse_nquad_doc; -use crate::make_factory_options; -use objio::{impl_has_options, HasOptions, ObjectReader}; -use rdftk_core::simple::data_set_factory; -use rdftk_core::{ - error::Error, - model::data_set::{DataSetFactoryRef, DataSetRef}, -}; +use objio::ObjectReader; +use rdftk_core::{error::Error, model::data_set::DataSet}; use std::io::Read; // ------------------------------------------------------------------------------------------------ // Public Types // ------------------------------------------------------------------------------------------------ -make_factory_options!(NQuadReaderOptions, DataSetFactoryRef, data_set_factory); - #[derive(Debug, Default)] -pub struct NQuadReader { - options: NQuadReaderOptions, -} +pub struct NQuadReader {} // ------------------------------------------------------------------------------------------------ // Implementations // ------------------------------------------------------------------------------------------------ -impl_has_options!(NQuadReader, NQuadReaderOptions); - -impl ObjectReader for NQuadReader { +impl ObjectReader for NQuadReader { type Error = Error; - fn read(&self, r: &mut R) -> Result + fn read(&self, r: &mut R) -> Result where R: Read, { let mut buffer = String::new(); r.read_to_string(&mut buffer)?; - let factory = self.options().factory().clone(); - parse_nquad_doc(buffer, factory) + parse_nquad_doc(buffer) } } diff --git a/rdftk_io/src/nq/writer.rs b/rdftk_io/src/nq/writer.rs index 79ab1ed..cbc403e 100644 --- a/rdftk_io/src/nq/writer.rs +++ b/rdftk_io/src/nq/writer.rs @@ -1,7 +1,7 @@ use objio::ObjectWriter; use rdftk_core::error::Error; -use rdftk_core::model::data_set::DataSetRef; -use rdftk_core::model::graph::named::NamedGraphRef; +use rdftk_core::model::data_set::DataSet; +use rdftk_core::model::graph::Graph; use std::io::Write; // ------------------------------------------------------------------------------------------------ @@ -18,14 +18,13 @@ pub struct NQuadWriter {} // Implementations // ------------------------------------------------------------------------------------------------ -impl ObjectWriter for NQuadWriter { +impl ObjectWriter for NQuadWriter { type Error = Error; - fn write(&self, w: &mut W, data_set: &DataSetRef) -> Result<(), Self::Error> + fn write(&self, w: &mut W, data_set: &DataSet) -> Result<(), Self::Error> where W: Write, { - let data_set = data_set.borrow(); for graph in data_set.graphs() { self.write(w, graph)?; } @@ -33,14 +32,13 @@ impl ObjectWriter for NQuadWriter { } } -impl ObjectWriter for NQuadWriter { +impl ObjectWriter for NQuadWriter { type Error = Error; - fn write(&self, w: &mut W, graph: &NamedGraphRef) -> Result<(), Self::Error> + fn write(&self, w: &mut W, graph: &Graph) -> Result<(), Self::Error> where W: Write, { - let graph = graph.borrow(); let graph_name = graph.name(); for subject in graph.subjects() { for predicate in graph.predicates_for(subject) { diff --git a/rdftk_io/src/nt/mod.rs b/rdftk_io/src/nt/mod.rs index 15bdee5..7d40541 100644 --- a/rdftk_io/src/nt/mod.rs +++ b/rdftk_io/src/nt/mod.rs @@ -9,8 +9,8 @@ format. ```rust use rdftk_io::nt::NTripleWriter; # use objio::ObjectWriter; -# use rdftk_core::model::graph::GraphRef; -# fn make_graph() -> GraphRef { rdftk_core::simple::graph::graph_factory().graph() } +# use rdftk_core::model::graph::Graph; +# fn make_graph() -> Graph { Graph::default() } let writer = NTripleWriter::default(); @@ -37,7 +37,7 @@ pub const MIME_TYPE: &str = "application/n-triples"; // ------------------------------------------------------------------------------------------------ mod reader; -pub use reader::{NTripleReader, NTripleReaderOptions}; +pub use reader::NTripleReader; mod writer; pub use writer::NTripleWriter; diff --git a/rdftk_io/src/nt/reader.rs b/rdftk_io/src/nt/reader.rs index e0f9b21..eb74e2a 100644 --- a/rdftk_io/src/nt/reader.rs +++ b/rdftk_io/src/nt/reader.rs @@ -1,38 +1,29 @@ use crate::common::parser::parse_ntriple_doc; -use crate::make_factory_options; -use objio::{impl_has_options, HasOptions, ObjectReader}; +use objio::ObjectReader; use rdftk_core::error::Error; -use rdftk_core::model::graph::{GraphFactoryRef, GraphRef}; -use rdftk_core::simple::graph_factory; +use rdftk_core::model::graph::Graph; use std::io::Read; // ------------------------------------------------------------------------------------------------ // Public Types // ------------------------------------------------------------------------------------------------ -make_factory_options!(NTripleReaderOptions, GraphFactoryRef, graph_factory); - #[derive(Debug, Default)] -pub struct NTripleReader { - options: NTripleReaderOptions, -} +pub struct NTripleReader {} // ------------------------------------------------------------------------------------------------ // Implementations // ------------------------------------------------------------------------------------------------ -impl_has_options!(NTripleReader, NTripleReaderOptions); - -impl ObjectReader for NTripleReader { +impl ObjectReader for NTripleReader { type Error = Error; - fn read(&self, r: &mut R) -> Result + fn read(&self, r: &mut R) -> Result where R: Read, { let mut buffer = String::new(); r.read_to_string(&mut buffer)?; - let factory = self.options().factory().clone(); - parse_ntriple_doc(buffer, factory) + parse_ntriple_doc(buffer) } } diff --git a/rdftk_io/src/nt/writer.rs b/rdftk_io/src/nt/writer.rs index b2de557..6bc8d2b 100644 --- a/rdftk_io/src/nt/writer.rs +++ b/rdftk_io/src/nt/writer.rs @@ -1,6 +1,6 @@ use objio::ObjectWriter; use rdftk_core::error::Error; -use rdftk_core::model::graph::GraphRef; +use rdftk_core::model::graph::Graph; use std::io::Write; // ------------------------------------------------------------------------------------------------ @@ -18,14 +18,13 @@ pub struct NTripleWriter {} // Implementations // ------------------------------------------------------------------------------------------------ -impl ObjectWriter for NTripleWriter { +impl ObjectWriter for NTripleWriter { type Error = Error; - fn write(&self, w: &mut W, object: &GraphRef) -> Result<(), Self::Error> + fn write(&self, w: &mut W, graph: &Graph) -> Result<(), Self::Error> where W: Write, { - let graph = object.borrow(); for subject in graph.subjects() { for predicate in graph.predicates_for(subject) { for object in graph.objects_for(subject, predicate) { diff --git a/rdftk_io/src/trig/mod.rs b/rdftk_io/src/trig/mod.rs index e5da534..9afba26 100644 --- a/rdftk_io/src/trig/mod.rs +++ b/rdftk_io/src/trig/mod.rs @@ -21,7 +21,7 @@ pub const MIME_TYPE: &str = "text/trig"; // ------------------------------------------------------------------------------------------------ mod reader; -pub use reader::{TrigReader, TrigReaderOptions}; +pub use reader::TrigReader; mod writer; pub use writer::{TrigWriter, TrigWriterOptions}; diff --git a/rdftk_io/src/trig/reader.rs b/rdftk_io/src/trig/reader.rs index b7232ae..c035b06 100644 --- a/rdftk_io/src/trig/reader.rs +++ b/rdftk_io/src/trig/reader.rs @@ -1,38 +1,29 @@ use crate::common::parser::parse_trig_doc; -use crate::make_factory_options; -use objio::{impl_has_options, HasOptions, ObjectReader}; +use objio::ObjectReader; use rdftk_core::error::Error; -use rdftk_core::model::data_set::{DataSetFactoryRef, DataSetRef}; -use rdftk_core::simple::data_set_factory; +use rdftk_core::model::data_set::DataSet; use std::io::Read; // ------------------------------------------------------------------------------------------------ // Public Types // ------------------------------------------------------------------------------------------------ -make_factory_options!(TrigReaderOptions, DataSetFactoryRef, data_set_factory); - #[derive(Debug, Default)] -pub struct TrigReader { - options: TrigReaderOptions, -} +pub struct TrigReader {} // ------------------------------------------------------------------------------------------------ // Implementations // ------------------------------------------------------------------------------------------------ -impl_has_options!(TrigReader, TrigReaderOptions); - -impl ObjectReader for TrigReader { +impl ObjectReader for TrigReader { type Error = Error; - fn read(&self, r: &mut R) -> Result + fn read(&self, r: &mut R) -> Result where R: Read, { let mut buffer = String::new(); r.read_to_string(&mut buffer)?; - let factory = self.options().factory().clone(); - parse_trig_doc(buffer, factory) + parse_trig_doc(buffer) } } diff --git a/rdftk_io/src/trig/writer.rs b/rdftk_io/src/trig/writer.rs index b158504..161da7a 100644 --- a/rdftk_io/src/trig/writer.rs +++ b/rdftk_io/src/trig/writer.rs @@ -1,7 +1,8 @@ -use crate::turtle::TurtleOptions; +use crate::turtle::TurtleWriterOptions; use objio::{impl_has_options, HasOptions, ObjectWriter}; use rdftk_core::error::Error; -use rdftk_core::model::{data_set::DataSetRef, graph::NamedGraphRef}; +use rdftk_core::model::data_set::DataSet; +use rdftk_core::model::graph::Graph; // ------------------------------------------------------------------------------------------------ // Public Types @@ -9,7 +10,7 @@ use rdftk_core::model::{data_set::DataSetRef, graph::NamedGraphRef}; #[derive(Debug, Default)] pub struct TrigWriterOptions { - turtle: TurtleOptions, + turtle: TurtleWriterOptions, omit_graph_keyword: bool, } @@ -22,8 +23,8 @@ pub struct TrigWriter { // Implementations // ------------------------------------------------------------------------------------------------ -impl From for TrigWriterOptions { - fn from(value: TurtleOptions) -> Self { +impl From for TrigWriterOptions { + fn from(value: TurtleWriterOptions) -> Self { Self { turtle: value, ..Default::default() @@ -31,8 +32,8 @@ impl From for TrigWriterOptions { } } -impl AsRef for TrigWriterOptions { - fn as_ref(&self) -> &TurtleOptions { +impl AsRef for TrigWriterOptions { + fn as_ref(&self) -> &TurtleWriterOptions { &self.turtle } } @@ -58,14 +59,13 @@ impl TrigWriterOptions { impl_has_options!(TrigWriter, TrigWriterOptions); -impl ObjectWriter for TrigWriter { +impl ObjectWriter for TrigWriter { type Error = Error; - fn write(&self, w: &mut W, data_set: &DataSetRef) -> Result<(), Self::Error> + fn write(&self, w: &mut W, data_set: &DataSet) -> Result<(), Self::Error> where W: std::io::Write, { - let data_set = data_set.borrow(); for graph in data_set.graphs() { self.write(w, graph)?; } @@ -73,14 +73,13 @@ impl ObjectWriter for TrigWriter { } } -impl ObjectWriter for TrigWriter { +impl ObjectWriter for TrigWriter { type Error = Error; - fn write(&self, w: &mut W, graph: &NamedGraphRef) -> Result<(), Self::Error> + fn write(&self, w: &mut W, graph: &Graph) -> Result<(), Self::Error> where W: std::io::prelude::Write, { - let graph = graph.borrow(); if let Some(name) = graph.name() { if !self.options().omit_graph_keyword() { w.write_all(b"GRAPH ")?; diff --git a/rdftk_io/src/turtle/mod.rs b/rdftk_io/src/turtle/mod.rs index fe8140a..e88fb26 100644 --- a/rdftk_io/src/turtle/mod.rs +++ b/rdftk_io/src/turtle/mod.rs @@ -9,15 +9,13 @@ An example, reading an existing NTriple file. ```rust use objio::{HasOptions, ObjectReader}; -use rdftk_io::nt::{NTripleReaderOptions, NTripleReader}; -use rdftk_core::simple::graph_factory; +use rdftk_io::nt::NTripleReader; use std::fs::File; use std::path::PathBuf; let file_path = PathBuf::from("tests/w3c/nt/literal.nt"); let mut file = File::open(file_path).unwrap(); -let reader = NTripleReader::default() - .with_options(NTripleReaderOptions::default().with_factory(graph_factory())); +let reader = NTripleReader::default(); let graph = reader.read(&mut file).unwrap(); ``` @@ -25,14 +23,14 @@ let graph = reader.read(&mut file).unwrap(); # Example Writer with Options ```rust -use rdftk_io::turtle::{TurtleWriter, TurtleOptions}; -use rdftk_iri::{IriRef, Iri}; +use rdftk_io::turtle::{TurtleWriter, TurtleWriterOptions}; +use rdftk_iri::Iri; use std::str::FromStr; # use objio::{HasOptions, ObjectWriter}; -# use rdftk_core::model::graph::GraphRef; -# fn make_graph() -> GraphRef { rdftk_core::simple::graph::graph_factory().graph() } +# use rdftk_core::model::graph::Graph; +# fn make_graph() -> Graph { Graph::default() } -let mut options = TurtleOptions::default() +let mut options = TurtleWriterOptions::default() .with_id_base(Iri::from_str("http://en.wikipedia.org/wiki/").unwrap().into()) .with_sparql_style() .without_nested_blank_nodes(); @@ -63,7 +61,7 @@ pub const MIME_TYPE: &str = "text/turtle"; // ------------------------------------------------------------------------------------------------ mod reader; -pub use reader::{TurtleReader, TurtleReaderOptions}; +pub use reader::TurtleReader; mod writer; -pub use writer::{TurtleOptions, TurtleWriter}; +pub use writer::{TurtleWriter, TurtleWriterOptions}; diff --git a/rdftk_io/src/turtle/reader.rs b/rdftk_io/src/turtle/reader.rs index 7b7b6d3..4532ae9 100644 --- a/rdftk_io/src/turtle/reader.rs +++ b/rdftk_io/src/turtle/reader.rs @@ -1,38 +1,29 @@ use crate::common::parser::parse_turtle_doc; -use crate::make_factory_options; -use objio::{impl_has_options, HasOptions, ObjectReader}; +use objio::ObjectReader; use rdftk_core::error::Error; -use rdftk_core::model::graph::{GraphFactoryRef, GraphRef}; -use rdftk_core::simple::graph_factory; +use rdftk_core::model::graph::Graph; use std::io::Read; // ------------------------------------------------------------------------------------------------ // Public Types // ------------------------------------------------------------------------------------------------ -make_factory_options!(TurtleReaderOptions, GraphFactoryRef, graph_factory); - #[derive(Debug, Default)] -pub struct TurtleReader { - options: TurtleReaderOptions, -} +pub struct TurtleReader {} // ------------------------------------------------------------------------------------------------ // Implementations // ------------------------------------------------------------------------------------------------ -impl_has_options!(TurtleReader, TurtleReaderOptions); - -impl ObjectReader for TurtleReader { +impl ObjectReader for TurtleReader { type Error = Error; - fn read(&self, r: &mut R) -> Result + fn read(&self, r: &mut R) -> Result where R: Read, { let mut buffer = String::new(); r.read_to_string(&mut buffer)?; - let factory = self.options().factory().clone(); - parse_turtle_doc(buffer, factory) + parse_turtle_doc(buffer) } } diff --git a/rdftk_io/src/turtle/writer.rs b/rdftk_io/src/turtle/writer.rs new file mode 100644 index 0000000..3400013 --- /dev/null +++ b/rdftk_io/src/turtle/writer.rs @@ -0,0 +1,765 @@ +use crate::common::indenter::Indenter; +use itertools::Itertools; +use objio::{impl_has_options, HasOptions, ObjectWriter}; +use rdftk_core::error::{Error, Result}; +use rdftk_core::model::graph::Graph; +use rdftk_core::model::statement::{ObjectNode, SubjectNode}; +use rdftk_iri::Iri; +use std::cell::RefCell; +use std::io::Write; +use std::str::FromStr; + +// ------------------------------------------------------------------------------------------------ +// Public Types +// ------------------------------------------------------------------------------------------------ + +/// +/// Options to change the behavior of the [`TurtleWriter`] implementation. +/// +#[derive(Clone, Debug)] +pub struct TurtleWriterOptions { + id_base: Option, + nest_blank_nodes: bool, + use_sparql_style: bool, + use_intellij_style: bool, + place_type_on_subject_line: bool, + convert_to_id_base: Option, + convert_base: Vec<(Iri, Iri)>, + indent_width: usize, +} + +/// +/// An implementation of `ObjectWriter` for Graphs. +/// +#[derive(Clone, Debug, Default)] +pub struct TurtleWriter { + options: TurtleWriterOptions, + context: RefCell, +} + +// ------------------------------------------------------------------------------------------------ +// Private Types +// ------------------------------------------------------------------------------------------------ + +#[derive(Clone, Copy, Debug, Default)] +struct WriterStatusFlags { + /// + /// `is_next_object_blank` is true when the next object in the series (also) + /// a blank node? In that case we do the formatting a bit different, + /// showing ],[` as the separator between blank nodes. + /// + is_next_object_blank: bool, + + /// + /// `is_being_sorted` is true when we're being called by a sorting algorithm + /// which means that we can only produce content on one (sortable) line, + /// avoid any line-feeds.. + /// + is_being_sorted: bool, + + /// + /// `is_last_of_subject` is true when we're working on the last triple of + /// the current subject, in which case we have to end a line with a dot + /// instead of a semicolon. + /// + is_last_of_subject: bool, + + /// + /// `is_last_of_predicate` is true when the current object is the last + /// object in the collection of objects for the given `subject + + /// predicate`. + /// + is_last_of_predicate: bool, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] +enum PredicateGroupOrdering { + /// + /// `rdf:type` triples come first and are written as `a`. + /// + Type, + /// + /// `rdfs:label` and/or `skos:prefLabel` triples come second. + /// + Label, + /// + /// `rdfs:comment` and/or `dc:description` triples come third. + /// + Comment, + /// + /// Everything else comes last. + /// + Other, +} + +#[derive(Clone, Debug, Default)] +struct WriterContext { + indenter: Indenter, + // TODO: make these BlankNode vectors + // TODO: make these references? + blanks_to_write: Vec, + blanks_written: Vec, +} + +// ------------------------------------------------------------------------------------------------ +// Implementations > Options +// ------------------------------------------------------------------------------------------------ + +impl Default for TurtleWriterOptions { + fn default() -> Self { + Self { + id_base: None, + nest_blank_nodes: true, + use_sparql_style: false, + use_intellij_style: false, + place_type_on_subject_line: false, + convert_to_id_base: None, + convert_base: Vec::new(), + indent_width: 2, + } + } +} + +impl TurtleWriterOptions { + /// + /// Return a new instance of the given `TurtleOptions` where the `id_base` is set to the given + /// Iri which will instruct the `TurtleWriter` to generate a `@base ` or `BASE ` + /// statement at the top of the file. + /// + pub fn with_id_base(self, id_base: Iri) -> Self { + Self { + id_base: Some(id_base.clone()), + ..self + } + } + + /// + /// Set default options to make the generated Turtle RDF look like it's formatted + /// by the LNKD.tech plugin that is used in the IntelliJ family of editors such as + /// Idea and CLion. + /// + /// This would allow you to load RDF from a git clone and write it back to disk + /// without causing unnecessary git-diff detectable changes. + /// + pub fn with_intellij_style(self) -> Self { + Self { + use_intellij_style: true, + indent_width: 4, + ..self + } + } + + pub fn with_sparql_style(self) -> Self { + Self { + use_sparql_style: true, + ..self + } + } + + pub fn with_indent_width(self, indent_width: usize) -> Self { + Self { + indent_width, + ..self + } + } + + pub fn with_nested_blank_nodes(self) -> Self { + Self { + nest_blank_nodes: true, + ..self + } + } + + pub fn without_nested_blank_nodes(self) -> Self { + Self { + nest_blank_nodes: false, + ..self + } + } + + pub fn id_base(&self) -> Option<&Iri> { + self.id_base.as_ref() + } + + pub fn set_id_base(&mut self, id_base: Iri) { + self.id_base = Some(id_base); + } + + pub fn unset_id_base(&mut self) { + self.id_base = None; + } + + pub fn nest_blank_nodes(&self) -> bool { + self.nest_blank_nodes + } + + pub fn set_nest_blank_nodes(&mut self, nest_blank_nodes: bool) { + self.nest_blank_nodes = nest_blank_nodes; + } + + pub fn use_sparql_style(&self) -> bool { + self.use_sparql_style + } + + pub fn set_use_sparql_style(&mut self, use_sparql_style: bool) { + self.use_sparql_style = use_sparql_style; + } + + /// + /// Use the same formatting style as used by the LNKD.tech editor plugin + /// for the IntelliJ IDEs like Idea and CLion + /// + pub fn use_intellij_style(&self) -> bool { + self.use_intellij_style + } + + pub fn set_use_intellij_style(&mut self, use_intellij_style: bool) { + self.use_intellij_style = use_intellij_style; + } + + /// + /// Some prefer to show the `rdf:type type` (or `a type`) statement on the same line as + /// the subject Iri. + /// + pub fn place_type_on_subject_line(&self) -> bool { + self.place_type_on_subject_line + } + + pub fn set_place_type_on_subject_line(&mut self, place_type_on_subject_line: bool) { + self.place_type_on_subject_line = place_type_on_subject_line; + } + + /// + /// If provided, any Iri that's written to Turtle that starts with the given + /// Iri will be written to Turtle as if it's part of the base namespace. + /// + pub fn convert_to_id_base(&self) -> Option<&Iri> { + self.convert_to_id_base.as_ref() + } + + pub fn set_convert_to_id_base(&mut self, convert_to_id_base: Iri) { + self.convert_to_id_base = Some(convert_to_id_base); + } + + pub fn unset_convert_to_id_base(&mut self) { + self.convert_to_id_base = None; + } + + /// + /// If provided, any Iri that's written to Turtle that starts with the given + /// Iri will be converted with the provided second base Iri. + /// + pub fn convert_base(&self) -> &Vec<(Iri, Iri)> { + &self.convert_base + } + + /// + /// Retrieve the indentation width, or the number of spaces to insert at each level of + /// indentation. + /// + pub fn indent_width(&self) -> usize { + self.indent_width + } + + /// + /// Set the indentation width, or the number of spaces to insert at each level of + /// indentation. This will panic if `indent_width` is zero. + /// + pub fn set_indent_width(&mut self, indent_width: usize) { + assert!(indent_width > 0); + self.indent_width = indent_width; + } +} + +// ------------------------------------------------------------------------------------------------ +// Implementations > Writer +// ------------------------------------------------------------------------------------------------ + +impl_has_options!(TurtleWriter, TurtleWriterOptions); + +impl ObjectWriter for TurtleWriter { + type Error = Error; + + fn write(&self, w: &mut W, graph: &Graph) -> Result<()> + where + W: Write, + { + { + let mut context_mut = self.context.borrow_mut(); + context_mut.indenter = + Indenter::default().with_default_indent_width(self.options.indent_width()); + context_mut.blanks_to_write = graph + .blank_node_subjects() + .iter() + .map(|s| { + let x = *s; + x.clone() + }) + .collect(); + context_mut.blanks_written = Default::default(); + } + + self.write_base_iri(w)?; + self.write_prefixes(w, graph)?; + let flags = WriterStatusFlags::default(); + self.write_normal_subjects(w, graph, flags)?; + // The given cursor object collects all the blank-node objects that have not + // been written to the turtle file yet but have been referred to during + // the call to `write_normal_subjects` above. Now process those + // unwritten blank nodes and add them to the end of the file. + self.write_blank_node_subjects(w, graph, flags) + } +} + +impl TurtleWriter { + fn indent(&self) { + let context = self.context.borrow(); + context.indenter.indent(); + } + + fn outdent(&self) { + let context = self.context.borrow(); + context.indenter.outdent(); + } + + fn new_line(&self, w: &mut W, flags: WriterStatusFlags) -> Result<()> { + if flags.is_being_sorted { + Ok(write!(w, " ")?) + } else { + Ok(write!(w, "\n{}", self.context.borrow().indenter)?) + } + } + + fn wrote_blank(&self, blank: &SubjectNode) { + assert!(blank.is_blank()); + self.context.borrow_mut().blanks_written.push(blank.clone()); + } + + //fn blanks_not_written(&self) -> HashSet { + // let context = self.context.borrow(); + // let blanks_written = &context.blanks_written; + // context + // .blanks_to_write + // .iter() + // .filter(|subject| !blanks_written.contains(subject)) + // .cloned() + // .collect() + //} + + fn sorted_subjects(&self, graph: &Graph) -> Vec { + graph + .node_subjects() + .into_iter() + .sorted() + .cloned() + .collect::>() + } + + /// Write out the graph base Iri in either turtle + /// style (as '@base ..') or SPARQL style (as 'BASE ...') + fn write_base_iri(&self, w: &mut W) -> Result<()> { + if let Some(base) = &self.options.id_base() { + if self.options.use_sparql_style() && !self.options.use_intellij_style() { + writeln!(w, "BASE <{}>", base.to_string().as_str())?; + } else { + writeln!(w, "@base <{}> .", base.to_string().as_str())?; + } + } + if !self.options.use_intellij_style() { + writeln!(w)?; + } + Ok(()) + } + + /// Write all prefix mappings, sort them by prefix to avoid + /// random order and unnecessary changes in git + fn write_prefixes(&self, w: &mut W, graph: &Graph) -> Result<()> { + let mappings = graph.prefix_mappings(); + for (prefix, namespace) in mappings.mappings().sorted() { + let prefix = prefix.as_ref().map(|n| n.as_ref()).unwrap_or(""); + let mut namespace_str = namespace.to_string(); + // If we have any base Iri conversions to do for any of the namespaces, then do + // it now: + for (from_base, to_base) in self.options.convert_base().iter() { + let from_base_str = from_base.to_string(); + if namespace_str.starts_with(from_base_str.as_str()) { + namespace_str = format!( + "{}{}", + to_base.to_string().as_str(), + &namespace_str[from_base_str.len()..] + ); + break; + } + } + if self.options.use_sparql_style() && !self.options.use_intellij_style() { + writeln!(w, "PREFIX {prefix}: <{namespace_str}>")?; + } else { + writeln!(w, "@prefix {prefix}: <{namespace_str}> .")?; + } + } + Ok(writeln!(w)?) + } + + //fn with_unwritten_blank_node_subjects( + // &self, + // w: &mut W, + // graph: &Graph, + // flags: WriterStatusFlags, + // f: F, + //) -> rdftk_core::error::Result<()> + //where + // F: Fn( + // &Self, + // &mut W, + // &Graph, + // SubjectNode, + // WriterStatusFlags, + // ) -> rdftk_core::error::Result<()>, + //{ + // for subject in self.blanks_not_written().into_iter() { + // self.context.borrow_mut().indenter.reset_depth(); + // f(self, w, graph, subject.clone(), flags)?; + // } + // Ok(()) + //} + + fn max_len_predicates(&self, graph: &Graph, predicates: &[&Iri]) -> Result { + let all_predicates_as_strings = predicates + .iter() + .map(|iri| self.compress_iri(graph, iri)) + .collect::>>()? + .iter() + .fold(0, |a, b| a.max(b.len())); + Ok(all_predicates_as_strings) + } + + fn object_sort_key(&self, graph: &Graph, object: &ObjectNode) -> Result { + let mut buffer = Vec::::new(); + let new_writer = Self::default().with_options(self.options.clone()); + let flags = WriterStatusFlags { + is_being_sorted: true, + ..Default::default() + }; + new_writer.write_object_content(&mut buffer, graph, object, flags)?; + Ok(String::from_utf8(buffer)?) + } + + fn write_object_content( + &self, + w: &mut W, + graph: &Graph, + object: &ObjectNode, + flags: WriterStatusFlags, + ) -> Result<()> { + if object.is_blank() { + if self.options.nest_blank_nodes() { + self.write_nested_blank_node(w, graph, object, flags)?; + } else { + write!(w, "_:{}", object.as_blank().unwrap())?; + } + } else if object.is_resource() { + self.write_iri(w, graph, object.as_resource().unwrap())?; + } else { + self.write_literal(w, object)?; + } + Ok(()) + } + + #[inline(always)] + fn write_iri(&self, w: &mut W, graph: &Graph, iri: &Iri) -> Result<()> { + Ok(write!(w, "{}", self.compress_iri(graph, iri)?)?) + } + + /// Compress any Iri to its "QName" given the supplied set of prefixes and + /// their namespace Iris. If we're encountering an Iri whose prefix + /// equals the given (optional) `convert_to_base` Iri then write it to + /// Turtle as if it's an Iri with the default base. + fn compress_iri(&self, graph: &Graph, iri: &Iri) -> Result { + let mut iri_str = iri.to_string(); + if let Some(id_base) = &self.options.id_base() { + if let Some(ref convert_to_id_base) = self.options.convert_to_id_base() { + let target_id_base = convert_to_id_base.to_string(); + if iri_str.starts_with(target_id_base.as_str()) { + return Ok(format!("<{}>", &iri_str[target_id_base.len()..])); + } + } + let id_base_str = id_base.to_string(); + if iri_str.starts_with(id_base_str.as_str()) { + return Ok(format!("<{}>", &iri_str[id_base_str.len()..])); + } + } + for (from_base, to_base) in self.options.convert_base().iter() { + let from_base_str = from_base.to_string(); + if iri_str.starts_with(from_base_str.as_str()) { + iri_str = format!( + "{}{}", + to_base.to_string().as_str(), + &iri_str[from_base_str.len()..] + ); + } + } + let iri = Iri::from_str(iri_str.as_str())?; + Ok(match graph.prefix_mappings().compress(&iri) { + None => format!("<{iri}>"), + Some(_qname) => format!("{_qname}"), + }) + } + + /// Write statements, start with those where subject is an Iri, + /// sort them by URL so that we keep a consistent result avoiding git-diff + /// to flag certain lines as changed. + fn write_normal_subjects( + &self, + w: &mut W, + graph: &Graph, + flags: WriterStatusFlags, + ) -> Result<()> { + for subject in self.sorted_subjects(graph) { + self.write_sub_graph(w, graph, &subject, flags)?; + writeln!(w)?; + } + Ok(()) + } + + /// Write statements where subject is a blank node + fn write_blank_node_subjects( + &self, + w: &mut W, + graph: &Graph, + flags: WriterStatusFlags, + ) -> Result<()> { + let context = self.context.borrow(); + for subject in context.blanks_to_write.iter() { + context.indenter.reset_depth(); + self.write_sub_graph(w, graph, subject, flags)?; + } + Ok(()) + } + + fn write_subject( + &self, + w: &mut W, + graph: &Graph, + subject: &SubjectNode, + flags: WriterStatusFlags, + ) -> Result<()> { + let depth = self.context.borrow().indenter.depth(); + if subject.is_blank() && depth == 0 { + if flags.is_being_sorted { + write!(w, " _:{}", subject.as_blank().unwrap())?; + } else { + write!(w, "\n_:{}", subject.as_blank().unwrap())?; + } + } else if subject.is_resource() { + self.write_iri(w, graph, subject.as_resource().unwrap())?; + } + self.indent(); + Ok(()) + } + + fn write_literal(&self, w: &mut W, literal: &ObjectNode) -> Result<()> { + // TODO: compress data type Iris + Ok(if let Some(literal) = literal.as_literal() { + write!(w, "{}", literal) + } else { + write!(w, "ERROR: this is not a literal: {:?}", literal) + }?) + } + + fn write_predicate( + &self, + w: &mut W, + graph: &Graph, + group: PredicateGroupOrdering, + predicate: &Iri, + max_len: usize, + flags: WriterStatusFlags, + ) -> Result<()> { + // Special treatment for `rdf:type`; show it in turtle as just "a" + // + if group == PredicateGroupOrdering::Type { + return if self.options.place_type_on_subject_line() { + Ok(write!(w, " a ")?) + } else { + self.new_line(w, flags)?; + Ok(write!(w, "{:( + &self, + w: &mut W, + graph: &Graph, + object: &ObjectNode, + max_len: usize, + flags: WriterStatusFlags, + ) -> Result<()> { + self.write_object_content(w, graph, object, flags)?; + if flags.is_last_of_predicate { + if flags.is_last_of_subject { + self.outdent(); + } + let depth = self.context.borrow().indenter.depth(); + if depth == 0 { + write!(w, " .")?; + self.new_line(w, flags)?; + } else if !flags.is_last_of_subject { + write!(w, " ;")?; + } + } else { + write!(w, ",")?; + if !flags.is_next_object_blank { + self.new_line(w, flags)?; + write!(w, "{:max_len$}", " ")?; + } + } + Ok(()) + } + + #[allow(clippy::too_many_arguments)] + fn write_predicate_object( + &self, + w: &mut W, + graph: &Graph, + group: PredicateGroupOrdering, + subject: &SubjectNode, + predicate: &Iri, + max_len: usize, + flags: WriterStatusFlags, + ) -> Result<()> { + // First, write the predicate + self.write_predicate(w, graph, group, predicate, max_len, flags)?; + + // Then, write the object(s) for that predicate (in sorted predictable order) + let mut objects = graph + .objects_for(subject, predicate) + .into_iter() + .collect_vec(); + let is_collection_of_objects = objects.len() > 1; + if is_collection_of_objects { + objects.sort_by_key(|o| self.object_sort_key(graph, o).unwrap_or_default()); + } + let mut o_iter = objects.iter().peekable(); + while let Some(object) = o_iter.next() { + let next_object = o_iter.peek(); + let flags = WriterStatusFlags { + is_next_object_blank: next_object.is_some() && next_object.unwrap().is_blank(), + is_last_of_predicate: next_object.is_none(), + ..flags + }; + self.write_object(w, graph, object, max_len, flags)?; + } + Ok(()) + } + + fn write_sub_graph( + &self, + w: &mut W, + graph: &Graph, + subject: &SubjectNode, + flags: WriterStatusFlags, + ) -> rdftk_core::error::Result<()> { + self.write_subject(w, graph, subject, flags)?; + self.write_predicates_of_subject(w, graph, subject, flags) + } + + fn write_predicates_of_subject( + &self, + w: &mut W, + graph: &Graph, + subject: &SubjectNode, + flags: WriterStatusFlags, + ) -> Result<()> { + let all_predicates = Vec::from_iter(graph.predicates_for(subject)); + let mut count = 0; + let total_number = all_predicates.len(); + let max_len = 1 + self.max_len_predicates(graph, &all_predicates)?; + + // let max_len = all_predicates.iter().(|) + // .fold(std::u16::MIN, |a,b| a.max(b.borrow().)); + for (group, ref mut preds) in PredicateGroupOrdering::group_predicates(&all_predicates) { + preds.sort_by_cached_key(|iri| self.compress_iri(graph, iri).unwrap()); + for predicate in preds { + count += 1; + let flags = WriterStatusFlags { + is_last_of_subject: count == total_number, + ..flags + }; + self.write_predicate_object(w, graph, group, subject, predicate, max_len, flags)?; + } + } + writeln!(w, " .")?; + + Ok(()) + } + + /// Deal with a nested blank node. + fn write_nested_blank_node( + &self, + w: &mut W, + graph: &Graph, + object: &ObjectNode, + flags: WriterStatusFlags, + ) -> Result<()> { + write!(w, "[")?; + let inner_subject = object.to_subject().unwrap(); + self.write_sub_graph(w, graph, &inner_subject, flags)?; + self.wrote_blank(&inner_subject); + self.new_line(w, flags)?; + write!(w, "]")?; + Ok(()) + } +} + +// ------------------------------------------------------------------------------------------------ +// Implementations > Ordering +// ------------------------------------------------------------------------------------------------ + +impl PredicateGroupOrdering { + fn group_predicates<'a>(predicates: &[&'a Iri]) -> Vec<(PredicateGroupOrdering, Vec<&'a Iri>)> { + let mut result = predicates + .iter() + .chunk_by(Self::group_predicate) + .into_iter() + .map(|(triple_type, group)| (triple_type, group.cloned().collect())) + .collect::)>>(); + result.sort_by_key(|a| a.0); + result + } + + fn group_predicate(predicate: &&&Iri) -> PredicateGroupOrdering { + match predicate.to_string().as_str() { + "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" => PredicateGroupOrdering::Type, + "http://www.w3.org/2000/01/rdf-schema#label" => PredicateGroupOrdering::Label, + "http://xmlns.com/foaf/0.1/name" => PredicateGroupOrdering::Label, + "http://purl.org/dc/elements/1.1/title" => PredicateGroupOrdering::Label, + "http://www.w3.org/2000/01/rdf-schema#comment" => PredicateGroupOrdering::Comment, + "http://purl.org/dc/elements/1.1/description" => PredicateGroupOrdering::Comment, + _ => PredicateGroupOrdering::Other, + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Unit Tests +// ------------------------------------------------------------------------------------------------ + +#[cfg(test)] +mod tests { + use super::PredicateGroupOrdering; + use super::PredicateGroupOrdering::*; + + #[test] + fn test_order() { + let mut v: Vec = vec![Comment, Label, Type, Other]; + v.sort(); + let sorted = format!("{:?}", v); + assert_eq!(sorted, "[Type, Label, Comment, Other]"); + } +} diff --git a/rdftk_io/src/turtle/writer/cursor.rs b/rdftk_io/src/turtle/writer/cursor.rs deleted file mode 100644 index 335d583..0000000 --- a/rdftk_io/src/turtle/writer/cursor.rs +++ /dev/null @@ -1,586 +0,0 @@ -use crate::{ - common::indenter::Indenter, - turtle::writer::{options::TurtleOptions, triple_type::TurtleTripleType}, -}; -use itertools::Itertools; -use rdftk_core::model::{ - graph::Graph, - qname::QName, - statement::{ObjectNode, ObjectNodeRef, SubjectNodeRef}, -}; -use rdftk_iri::{Iri, IriRef}; -#[allow(unused_imports)] -use std::borrow::BorrowMut as _; -use std::{ - cell::{Ref, RefCell}, - collections::HashSet, - io::Write, - iter::FromIterator, - ops::{Deref, DerefMut}, - rc::Rc, - str::FromStr, -}; - -type IsNextObjectBlankNode = bool; -type IsBeingSorted = bool; -type IsLastOfSubject = bool; -type IsLastOfPredicate = bool; - -#[derive(Default, Copy, Clone, Debug)] -struct TurtleCursorFlags { - /// `is_next_object_blank` is true when the next object in the series (also) - /// a blank node? In that case we do the formatting a bit different, - /// showing ],[` as the separator between blank nodes. - is_next_object_blank: IsNextObjectBlankNode, - /// `is_being_sorted` is true when we're being called by a sorting algorithm - /// which means that we can only produce content on one (sortable) line, - /// avoid any line-feeds.. - is_being_sorted: IsBeingSorted, - /// `is_last_of_subject` is true when we're working on the last triple of - /// the current subject, in which case we have to end a line with a dot - /// instead of a semicolon. - is_last_of_subject: IsLastOfSubject, - /// `is_last_of_predicate` is true when the current object is the last - /// object in the collection of objects for the given `subject + - /// predicate`. - is_last_of_predicate: IsLastOfPredicate, -} - -pub(crate) struct TurtleCursor<'a, W> -where - W: Write + Sized, -{ - w: Rc>, - graph: Rc>, - indenter: RefCell, - blanks_to_write: RefCell>, - blanks_written: RefCell>, - options: TurtleOptions, -} - -impl<'a, W: Write + Sized> TurtleCursor<'a, W> { - pub(crate) fn new( - w: Rc>, - graph: Rc>, - options: TurtleOptions, - ) -> Self { - let indenter = RefCell::new(Indenter::default().with_width(options.indent_width())); - let blanks_to_write = RefCell::new( - graph - .deref() - .blank_node_subjects() - .iter() - .map(|s| { - let x = *s; - x.clone() - }) - .collect(), - ); - let blanks_written: RefCell> = RefCell::new(Default::default()); - Self { - w, - graph, - indenter, - blanks_to_write, - blanks_written, - options, - } - } - - /// Get a copy (you clone it first) of the current Cursor but then with the - /// given writer. - fn new_with_writer( - w: Rc>, - other: &'a TurtleCursor<'a, W2>, - ) -> Self { - Self { - w, - graph: other.graph.clone(), - indenter: { - let clone = other.indenter.borrow().clone().with_depth(0); - RefCell::new(clone) - }, - blanks_to_write: other.blanks_to_write.clone(), - blanks_written: other.blanks_written.clone(), - options: other.options.clone(), - } - } - - pub(crate) fn write(&self) -> rdftk_core::error::Result<()> { - self.write_base_iri()?; - self.write_prefixes()?; - let flags = TurtleCursorFlags::default(); - self.write_normal_subjects(flags)?; - // The given cursor object collects all the blank-node objects that have not - // been written to the turtle file yet but have been referred to during - // the call to `write_normal_subjects` above. Now process those - // unwritten blank nodes and add them to the end of the file. - self.write_blank_node_subjects(flags) - } - - #[allow(unused_results)] - fn indent(&self) -> &RefCell { - self.indenter.replace_with(|old| old.indent()); - &self.indenter - } - - #[allow(unused_results)] - #[allow(unused)] - fn outdent(&self) -> &RefCell { - self.indenter.replace_with(|old| old.outdent()); - &self.indenter - } - - fn new_line(&self, flags: TurtleCursorFlags) -> rdftk_core::error::Result<()> { - if flags.is_being_sorted { - Ok(write!(self, " ")?) - } else { - Ok(write!(self, "\n{}", self.indenter.borrow())?) - } - } - - fn write_fmt(&self, fmt: std::fmt::Arguments<'_>) -> std::io::Result<()> { - self.w.as_ref().borrow_mut().write_fmt(fmt) - } - - fn wrote_blank(&self, blank: &SubjectNodeRef) { - assert!(blank.is_blank()); - self.blanks_written.borrow_mut().push(blank.clone()); - } - - fn blanks_not_written(&self) -> HashSet { - let blanks_written = self.blanks_written.borrow(); - self.blanks_to_write - .borrow() - .iter() - .filter(|subject| !blanks_written.contains(subject)) - .cloned() - .collect() - } - - fn sorted_subjects(&self) -> Vec { - self.graph - .node_subjects() - .into_iter() - .sorted() - .cloned() - .collect::>() - } - - /// Write out the graph base Iri in either turtle - /// style (as '@base ..') or SPARQL style (as 'BASE ...') - fn write_base_iri(&self) -> rdftk_core::error::Result<()> { - if let Some(base) = &self.options.id_base() { - if self.options.use_sparql_style() && !self.options.use_intellij_style() { - writeln!(self, "BASE <{}>", base.to_string().as_str())?; - } else { - writeln!(self, "@base <{}> .", base.to_string().as_str())?; - } - } - if !self.options.use_intellij_style() { - writeln!(self)?; - } - Ok(()) - } - - /// Write all prefix mappings, sort them by prefix to avoid - /// random order and unnecessary changes in git - fn write_prefixes(&self) -> rdftk_core::error::Result<()> { - let mappings = self.graph.deref().prefix_mappings(); - let mappings = mappings.try_borrow()?; - for (prefix, namespace) in mappings.mappings().sorted() { - let prefix = prefix.as_ref().map(|n| n.as_ref()).unwrap_or(""); - let mut namespace_str = namespace.to_string(); - // If we have any base Iri conversions to do for any of the namespaces, then do - // it now: - for (from_base, to_base) in self.options.convert_base().iter() { - let from_base_str = from_base.to_string(); - if namespace_str.starts_with(from_base_str.as_str()) { - namespace_str = format!( - "{}{}", - to_base.to_string().as_str(), - &namespace_str[from_base_str.len()..] - ); - break; - } - } - if self.options.use_sparql_style() && !self.options.use_intellij_style() { - writeln!(self, "PREFIX {prefix}: <{namespace_str}>")?; - } else { - writeln!(self, "@prefix {prefix}: <{namespace_str}> .")?; - } - } - Ok(writeln!(self)?) - } - - fn with_node_subjects_do( - &self, - flags: TurtleCursorFlags, - f: F, - ) -> rdftk_core::error::Result<()> - where - F: Fn(&Self, &SubjectNodeRef, TurtleCursorFlags) -> rdftk_core::error::Result<()>, - { - for subject in self.sorted_subjects() { - f(self, &subject, flags)?; - } - Ok(()) - } - - fn with_unwritten_blank_node_subjects( - &self, - flags: TurtleCursorFlags, - f: F, - ) -> rdftk_core::error::Result<()> - where - F: Fn(&Self, SubjectNodeRef, TurtleCursorFlags) -> rdftk_core::error::Result<()>, - { - for subject in self.blanks_not_written().into_iter() { - self.indenter.borrow_mut().reset_depth(); - f(self, subject.clone(), flags)?; - } - Ok(()) - } - - fn with_predicates_grouped( - &self, - subject: &SubjectNodeRef, - flags: TurtleCursorFlags, - f: F, - ) -> rdftk_core::error::Result<()> - where - F: Fn( - &Self, - TurtleTripleType, - &IriRef, - usize, - TurtleCursorFlags, - ) -> rdftk_core::error::Result<()>, - { - let all_predicates = Vec::from_iter(self.graph.predicates_for(subject)); - let mut count = 0; - let total_number = all_predicates.len(); - let max_len = 1 + self.max_len_predicates(&all_predicates)?; - - // let max_len = all_predicates.iter().(|) - // .fold(std::u16::MIN, |a,b| a.max(b.borrow().)); - for (group, ref mut preds) in TurtleTripleType::group_predicates(&all_predicates) { - preds.sort_by_cached_key(|iri| self.predicate_iri_as_string(iri).unwrap()); - for predicate in preds { - count += 1; - let flags = TurtleCursorFlags { - is_last_of_subject: count == total_number, - ..flags - }; - f(self, group, predicate, max_len, flags)?; - } - } - - Ok(()) - } - - fn max_len_predicates(&self, predicates: &[&IriRef]) -> rdftk_core::error::Result { - let all_predicates_as_strings = predicates - .iter() - .map(|iri| { - let mut buffer = Vec::::new(); - self.compress_iri(&mut buffer, iri)?; - Ok(String::from_utf8(buffer)?) - }) - .collect::, rdftk_core::error::Error>>()? - .iter() - .fold(0, |a, b| a.max(b.len())); - Ok(all_predicates_as_strings) - } - - /// Iterate through all sorted objects of the given subject and predicate - fn with_objects( - &self, - subject: &SubjectNodeRef, - predicate: &IriRef, - flags: TurtleCursorFlags, - f: F, - ) -> rdftk_core::error::Result<()> - where - F: Fn(&Self, &ObjectNodeRef, TurtleCursorFlags) -> rdftk_core::error::Result<()>, - { - let mut objects = self - .graph - .deref() - .objects_for(subject, predicate) - .into_iter() - .collect_vec(); - let is_collection_of_objects = objects.len() > 1; - if is_collection_of_objects { - objects.sort_by_key(|o| self.object_sort_key(o).unwrap_or_default()); - } - let mut o_iter = objects.iter().peekable(); - while let Some(object) = o_iter.next() { - let next_object = o_iter.peek(); - let flags = TurtleCursorFlags { - is_next_object_blank: next_object.is_some() && next_object.unwrap().is_blank(), - is_last_of_predicate: next_object.is_none(), - ..flags - }; - f(self, object, flags)?; - } - Ok(()) - } - - fn object_sort_key(&self, object: &ObjectNodeRef) -> rdftk_core::error::Result { - let buffer = Rc::new(RefCell::from(Vec::::new())); - let new_cursor = TurtleCursor::new_with_writer(buffer.clone(), self); - let flags = TurtleCursorFlags { - is_being_sorted: true, - ..Default::default() - }; - new_cursor.write_object_content(object, flags)?; - Ok(String::from_utf8(buffer.take())?) - } - - fn write_object_content( - &self, - object: &ObjectNodeRef, - flags: TurtleCursorFlags, - ) -> rdftk_core::error::Result<()> { - if object.is_blank() { - if self.options.nest_blank_nodes() { - self.write_nested_blank_node(object, flags)?; - } else { - write!(self, "_:{}", object.as_blank().unwrap())?; - } - } else if object.is_iri() { - self.write_iri(object.as_iri().unwrap())?; - } else { - self.write_literal(object)?; - } - Ok(()) - } - - /// Write out a given Iri as Turtle. - /// Compress any Iri to its "QName" given the supplied set of prefixes and - /// their namespace Iris. If we're encountering an Iri whose prefix - /// equals the given (optional) `convert_to_base` Iri then write it to - /// Turtle as if it's an Iri with the default base. - fn write_iri(&self, iri: &IriRef) -> std::io::Result<()> { - self.compress_iri(self.w.deref().borrow_mut().deref_mut(), iri) - } - - fn compress_iri( - &self, - writer: &mut W2, - iri: &IriRef, - ) -> std::io::Result<()> { - let mut iri_str = iri.to_string(); - if let Some(id_base) = &self.options.id_base() { - if let Some(ref convert_to_id_base) = self.options.convert_to_id_base() { - let target_id_base = convert_to_id_base.to_string(); - if iri_str.starts_with(target_id_base.as_str()) { - return write!(writer, "<{}>", &iri_str[target_id_base.len()..]); - } - } - let id_base_str = id_base.to_string(); - if iri_str.starts_with(id_base_str.as_str()) { - return write!(writer, "<{}>", &iri_str[id_base_str.len()..]); - } - } - for (from_base, to_base) in self.options.convert_base().iter() { - let from_base_str = from_base.to_string(); - if iri_str.starts_with(from_base_str.as_str()) { - iri_str = format!( - "{}{}", - to_base.to_string().as_str(), - &iri_str[from_base_str.len()..] - ); - } - } - let iri = IriRef::new(Iri::from_str(iri_str.as_str()).unwrap()); - match self.compress(&iri) { - None => write!(writer, "<{iri}>"), - Some(_qname) => write!(writer, "{_qname}"), - } - } - - /// Write statements, start with those where subject is an Iri, - /// sort them by URL so that we keep a consistent result avoiding git-diff - /// to flag certain lines as changed. - fn write_normal_subjects(&self, flags: TurtleCursorFlags) -> rdftk_core::error::Result<()> { - self.with_node_subjects_do(flags, |c, subject, flags| { - c.write_sub_graph(subject, flags)?; - writeln!(c)?; - Ok(()) - }) - } - - /// Write statements where subject is a blank node - fn write_blank_node_subjects(&self, flags: TurtleCursorFlags) -> rdftk_core::error::Result<()> { - self.with_unwritten_blank_node_subjects(flags, |c, ref subject, flags| { - c.write_sub_graph(subject, flags)?; - Ok(()) - }) - } - - fn write_subject( - &self, - subject: SubjectNodeRef, - flags: TurtleCursorFlags, - ) -> rdftk_core::error::Result<()> { - if subject.is_blank() && self.indenter.borrow().depth() == 0 { - if flags.is_being_sorted { - write!(self, " _:{}", subject.as_blank().unwrap())?; - } else { - write!(self, "\n_:{}", subject.as_blank().unwrap())?; - } - } else if subject.is_iri() { - self.write_iri(subject.as_iri().unwrap())?; - } - let _ = self.indent(); - Ok(()) - } - - /// Compress an Iri into a qname, if possible. - fn compress(&self, iri: &IriRef) -> Option { - self.graph - .deref() - .prefix_mappings() - .deref() - .borrow() - .compress(iri) - } - - fn write_literal(&self, literal: &ObjectNodeRef) -> std::io::Result<()> { - // TODO: compress data type Iris - if let Some(literal) = literal.as_literal() { - write!(self, "{}", literal) - } else { - write!(self, "ERROR: this is not a literal: {:?}", literal) - } - } - - fn write_predicate( - &self, - group: TurtleTripleType, - predicate: &IriRef, - max_len: usize, - flags: TurtleCursorFlags, - ) -> rdftk_core::error::Result<()> { - // Special treatment for `rdf:type`; show it in turtle as just "a" - // - if group == TurtleTripleType::Type { - return if self.options.place_type_on_subject_line() { - Ok(write!(self, " a ")?) - } else { - self.new_line(flags)?; - Ok(write!(self, "{: rdftk_core::error::Result { - let buffer = Rc::new(RefCell::from(Vec::::new())); - let new_cursor = TurtleCursor::new_with_writer(buffer.clone(), self); - new_cursor.write_iri(predicate)?; - Ok(String::from_utf8(buffer.take())?) - } - - fn write_object( - &self, - object: &ObjectNodeRef, - max_len: usize, - flags: TurtleCursorFlags, - ) -> rdftk_core::error::Result<()> { - self.write_object_content(object, flags)?; - if flags.is_last_of_predicate { - if flags.is_last_of_subject { - let _ = self.outdent(); - } - if self.indenter.borrow().depth() == 0 { - write!(self, " .")?; - self.new_line(flags)?; - } else if !flags.is_last_of_subject { - write!(self, " ;")?; - } - } else { - write!(self, ",")?; - if !flags.is_next_object_blank { - self.new_line(flags)?; - write!(self, "{:max_len$}", " ")?; - } - } - Ok(()) - } - - fn write_predicate_object( - &self, - group: TurtleTripleType, - subject: &SubjectNodeRef, - predicate: &IriRef, - max_len: usize, - flags: TurtleCursorFlags, - ) -> rdftk_core::error::Result<()> { - // First, write the predicate - // - self.write_predicate(group, predicate, max_len, flags)?; - // Then, write the object(s) for that predicate (in sorted predictable order) - // - self.with_objects(subject, predicate, flags, |c, object, flags| { - c.write_object(object, max_len, flags) - }) - } - - fn write_sub_graph( - &self, - subject: &SubjectNodeRef, - flags: TurtleCursorFlags, - ) -> rdftk_core::error::Result<()> { - self.write_subject(subject.clone(), flags)?; - self.write_predicates_of_subject(subject.clone(), flags) - } - - fn write_predicates_of_subject( - &self, - subject: SubjectNodeRef, - flags: TurtleCursorFlags, - ) -> rdftk_core::error::Result<()> { - self.with_predicates_grouped(&subject, flags, |c, group, predicate, max_len, flags| { - c.write_predicate_object(group, &subject, predicate, max_len, flags) - }) - } - - /// Deal with a nested blank node. - fn write_nested_blank_node( - &self, - object: &ObjectNodeRef, - flags: TurtleCursorFlags, - ) -> rdftk_core::error::Result<()> { - write!(self, "[")?; - let inner_subject: SubjectNodeRef = self - .graph - .statement_factory() - .object_as_subject(<&Rc>::clone(&object).clone()) - .unwrap(); - self.write_sub_graph(&inner_subject, flags)?; - self.wrote_blank(&inner_subject); - self.new_line(flags)?; - write!(self, "]")?; - Ok(()) - } -} - -impl Write for &TurtleCursor<'_, W> -where - W: Write + Sized, -{ - fn write(&mut self, buf: &[u8]) -> std::io::Result { - self.w.deref().borrow_mut().write(buf) - } - - fn flush(&mut self) -> std::io::Result<()> { - self.w.deref().borrow_mut().flush() - } -} diff --git a/rdftk_io/src/turtle/writer/mod.rs b/rdftk_io/src/turtle/writer/mod.rs deleted file mode 100644 index d6f3e27..0000000 --- a/rdftk_io/src/turtle/writer/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -// ------------------------------------------------------------------------------------------------ -// Modules -// ------------------------------------------------------------------------------------------------ - -mod cursor; - -mod options; -pub use options::TurtleOptions; - -mod triple_type; - -mod turtle_writer; -pub use turtle_writer::TurtleWriter; diff --git a/rdftk_io/src/turtle/writer/options.rs b/rdftk_io/src/turtle/writer/options.rs deleted file mode 100644 index 018a023..0000000 --- a/rdftk_io/src/turtle/writer/options.rs +++ /dev/null @@ -1,157 +0,0 @@ -use rdftk_iri::IriRef; - -#[derive(Clone, Debug)] -pub struct TurtleOptions { - id_base: Option, - nest_blank_nodes: bool, - use_sparql_style: bool, - use_intellij_style: bool, - place_type_on_subject_line: bool, - convert_to_id_base: Option, - convert_base: Vec<(IriRef, IriRef)>, - indent_width: usize, -} - -impl Default for TurtleOptions { - fn default() -> Self { - Self { - id_base: None, - nest_blank_nodes: true, - use_sparql_style: false, - use_intellij_style: false, - place_type_on_subject_line: false, - convert_to_id_base: None, - convert_base: Vec::new(), - indent_width: 2, - } - } -} - -impl TurtleOptions { - /// Return a new instance of the given `TurtleOptions` where the `id_base` is set to the given - /// Iri which will instruct the `TurtleWriter` to generate a `@base ` or `BASE ` - /// statement at the top of the file. - pub fn with_id_base(self, id_base: IriRef) -> Self { - Self { - id_base: Some(id_base.clone()), - ..self - } - } - - /// Set default options to make the generated Turtle RDF look like it's formatted - /// by the LNKD.tech plugin that is used in the IntelliJ family of editors such as - /// Idea and CLion. - /// This would allow you to load RDF from a git clone and write it back to disk - /// without causing unnecessary git-diff detectable changes. - pub fn with_intellij_style(self) -> Self { - Self { - use_intellij_style: true, - indent_width: 4, - ..self - } - } - - pub fn with_sparql_style(self) -> Self { - Self { - use_sparql_style: true, - ..self - } - } - - pub fn with_indent_width(self, indent_width: usize) -> Self { - Self { - indent_width, - ..self - } - } - - pub fn with_nested_blank_nodes(self) -> Self { - Self { - nest_blank_nodes: true, - ..self - } - } - - pub fn without_nested_blank_nodes(self) -> Self { - Self { - nest_blank_nodes: false, - ..self - } - } - - pub fn id_base(&self) -> Option<&IriRef> { - self.id_base.as_ref() - } - - pub fn set_id_base(&mut self, id_base: IriRef) { - self.id_base = Some(id_base); - } - - pub fn unset_id_base(&mut self) { - self.id_base = None; - } - - pub fn nest_blank_nodes(&self) -> bool { - self.nest_blank_nodes - } - - pub fn set_nest_blank_nodes(&mut self, nest_blank_nodes: bool) { - self.nest_blank_nodes = nest_blank_nodes; - } - - pub fn use_sparql_style(&self) -> bool { - self.use_sparql_style - } - - pub fn set_use_sparql_style(&mut self, use_sparql_style: bool) { - self.use_sparql_style = use_sparql_style; - } - - /// Use the same formatting style as used by the LNKD.tech editor plugin - /// for the IntelliJ IDEs like Idea and CLion - pub fn use_intellij_style(&self) -> bool { - self.use_intellij_style - } - - pub fn set_use_intellij_style(&mut self, use_intellij_style: bool) { - self.use_intellij_style = use_intellij_style; - } - - /// Some prefer to show the "a " statement on the same line as - /// the subject Iri. - pub fn place_type_on_subject_line(&self) -> bool { - self.place_type_on_subject_line - } - - pub fn set_place_type_on_subject_line(&mut self, place_type_on_subject_line: bool) { - self.place_type_on_subject_line = place_type_on_subject_line; - } - - /// If provided, any Iri that's written to Turtle that starts with the given - /// Iri will be written to Turtle as if it's part of the base namespace. - pub fn convert_to_id_base(&self) -> Option<&IriRef> { - self.convert_to_id_base.as_ref() - } - - pub fn set_convert_to_id_base(&mut self, convert_to_id_base: IriRef) { - self.convert_to_id_base = Some(convert_to_id_base); - } - - pub fn unset_convert_to_id_base(&mut self) { - self.convert_to_id_base = None; - } - - /// If provided, any Iri that's written to Turtle that starts with the given - /// Iri will be converted with the provided second base Iri. - pub fn convert_base(&self) -> &Vec<(IriRef, IriRef)> { - &self.convert_base - } - - pub fn indent_width(&self) -> usize { - self.indent_width - } - - pub fn set_indent_width(&mut self, indent_width: usize) { - self.indent_width = indent_width; - } -} diff --git a/rdftk_io/src/turtle/writer/triple_type.rs b/rdftk_io/src/turtle/writer/triple_type.rs deleted file mode 100644 index 3c469c6..0000000 --- a/rdftk_io/src/turtle/writer/triple_type.rs +++ /dev/null @@ -1,55 +0,0 @@ -use itertools::Itertools; -use rdftk_iri::IriRef; - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] -pub(crate) enum TurtleTripleType { - /// rdf:type triples come first and are written as 'a' - Type, - /// rdfs:label and/or skos:prefLabel triples come second - Label, - /// rdfs:comment and/or dct:description triples come third - Comment, - /// Everything else comes last - Other, -} - -impl TurtleTripleType { - pub(crate) fn group_predicates<'a>( - predicates: &[&'a IriRef], - ) -> Vec<(TurtleTripleType, Vec<&'a IriRef>)> { - let mut result = predicates - .iter() - .chunk_by(Self::group_predicate) - .into_iter() - .map(|(triple_type, group)| (triple_type, group.cloned().collect())) - .collect::)>>(); - result.sort_by_key(|a| a.0); - result - } - - fn group_predicate(predicate: &&&IriRef) -> TurtleTripleType { - match predicate.to_string().as_str() { - "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" => TurtleTripleType::Type, - "http://www.w3.org/2000/01/rdf-schema#label" => TurtleTripleType::Label, - "http://xmlns.com/foaf/0.1/name" => TurtleTripleType::Label, - "http://purl.org/dc/elements/1.1/title" => TurtleTripleType::Label, - "http://www.w3.org/2000/01/rdf-schema#comment" => TurtleTripleType::Comment, - "http://purl.org/dc/elements/1.1/description" => TurtleTripleType::Comment, - _ => TurtleTripleType::Other, - } - } -} - -#[cfg(test)] -mod tests { - use super::TurtleTripleType; - use super::TurtleTripleType::*; - - #[test] - fn test_order() { - let mut v: Vec = vec![Comment, Label, Type, Other]; - v.sort(); - let sorted = format!("{:?}", v); - assert_eq!(sorted, "[Type, Label, Comment, Other]"); - } -} diff --git a/rdftk_io/src/turtle/writer/turtle_writer.rs b/rdftk_io/src/turtle/writer/turtle_writer.rs deleted file mode 100644 index 400f445..0000000 --- a/rdftk_io/src/turtle/writer/turtle_writer.rs +++ /dev/null @@ -1,33 +0,0 @@ -use crate::turtle::writer::cursor::TurtleCursor; -use crate::turtle::writer::options::TurtleOptions; -use objio::{impl_has_options, ObjectWriter}; -use rdftk_core::error::Error; -use rdftk_core::model::graph::GraphRef; -use std::cell::RefCell; -use std::io::Write; -use std::ops::Deref; -use std::rc::Rc; - -#[derive(Clone, Debug, Default)] -pub struct TurtleWriter { - options: TurtleOptions, -} - -impl_has_options!(TurtleWriter, TurtleOptions); - -impl ObjectWriter for TurtleWriter { - type Error = Error; - - fn write(&self, w: &mut W, graph: &GraphRef) -> Result<(), Error> - where - W: Write, - { - let cursor = TurtleCursor::new( - Rc::new(RefCell::from(w)), - Rc::from(graph.deref().borrow()), - self.options.clone(), - ); - - cursor.write() - } -} diff --git a/rdftk_io/src/xml/mod.rs b/rdftk_io/src/xml/mod.rs index 782d182..c72713a 100644 --- a/rdftk_io/src/xml/mod.rs +++ b/rdftk_io/src/xml/mod.rs @@ -11,13 +11,14 @@ in the specification, "flat" or "striped". ```rust use rdftk_io::xml::{XmlOptions, XmlWriter}; # use objio::{HasOptions, ObjectWriter}; -# let graph = rdftk_core::simple::graph::graph_factory().graph(); +# use rdftk_core::model::graph::Graph; +# fn make_graph() -> Graph { Graph::default() } let options: XmlOptions = XmlOptions::default().flat().pretty(); let writer = XmlWriter::default().with_options(options); -println!("{}", writer.write_to_string(&graph).unwrap()); +println!("{}", writer.write_to_string(&make_graph()).unwrap()); ``` */ diff --git a/rdftk_io/src/xml/reader.rs b/rdftk_io/src/xml/reader.rs index 965e4f6..3f76300 100644 --- a/rdftk_io/src/xml/reader.rs +++ b/rdftk_io/src/xml/reader.rs @@ -5,11 +5,10 @@ use crate::xml::syntax::{ }; use objio::ObjectReader; use rdftk_core::error::{invalid_state_error, Error}; -use rdftk_core::model::graph::{GraphFactoryRef, GraphRef}; -use rdftk_core::model::literal::{DataType, LanguageTag}; -use rdftk_core::model::statement::SubjectNodeRef; -use rdftk_core::simple::graph_factory; -use rdftk_iri::{Iri, IriRef}; +use rdftk_core::model::graph::Graph; +use rdftk_core::model::literal::{DataType, LanguageTag, Literal}; +use rdftk_core::model::statement::{BlankNode, Statement, SubjectNode}; +use rdftk_iri::Iri; use rdftk_names::rdf; use std::io::Read; use std::str::FromStr; @@ -42,7 +41,7 @@ struct ExpectedName { #[derive(Clone, Debug)] enum SubjectType { BlankNamed(String), - Resource(IriRef), + Resource(Iri), RelativeResource(String), } @@ -57,10 +56,10 @@ enum ParseType { struct Attributes<'a> { subject_type: Option, parse_type: Option, - uri_base: Option, - data_type: Option, + uri_base: Option, + data_type: Option, language: Option, - resource: Option, + resource: Option, inner: Vec<&'a OwnedAttribute>, } @@ -68,15 +67,15 @@ struct Attributes<'a> { // Implementations // ------------------------------------------------------------------------------------------------ -impl ObjectReader for XmlReader { +impl ObjectReader for XmlReader { type Error = Error; - fn read(&self, r: &mut R) -> Result + fn read(&self, r: &mut R) -> Result where R: Read, { let mut event_reader = EventReader::new(r); - parse_document(&mut event_reader, graph_factory()) + parse_document(&mut event_reader) } } @@ -131,11 +130,8 @@ macro_rules! error_event { }; } -fn parse_document( - event_reader: &mut EventReader<&mut R>, - factory: GraphFactoryRef, -) -> Result { - let mut graph = factory.graph(); +fn parse_document(event_reader: &mut EventReader<&mut R>) -> Result { + let mut graph = Graph::default(); let rdf_element = ExpectedName::new(ELEMENT_RDF, rdf::namespace_str()); loop { @@ -175,12 +171,12 @@ fn parse_document( fn parse_subject_element( event_reader: &mut EventReader<&mut R>, - xml_base: &Option, - _subject: Option<&SubjectNodeRef>, - graph: &mut GraphRef, -) -> Result, Error> { + xml_base: &Option, + _subject: Option<&SubjectNode>, + graph: &mut Graph, +) -> Result, Error> { let description_element = ExpectedName::new(ELEMENT_DESCRIPTION, rdf::namespace_str()); - let mut subject: Option = None; + let mut subject: Option = None; loop { let event = event_reader.next(); match &event { @@ -191,48 +187,32 @@ fn parse_subject_element( }) => { trace_event!("parse_subject_element" => event); let attributes = parse_attributes(attributes)?; - let subject_node = match &attributes.subject_type { + let subject_node: SubjectNode = match &attributes.subject_type { None => { // SPEC: §2.1 Introduction - graph.borrow().statement_factory().blank_subject_new() + BlankNode::generate().into() } Some(SubjectType::Resource(subject)) => { // SPEC: §2.2 Node Elements and Property Elements - graph - .borrow() - .statement_factory() - .named_subject(subject.clone()) + subject.clone().into() } Some(SubjectType::RelativeResource(subject)) => { // SPEC: 2.14 Abbreviating URIs: rdf:ID and xml:base let uri = format!("{}{}", xml_base.as_ref().unwrap(), subject); - graph - .borrow() - .statement_factory() - .named_subject(value_to_iri(&uri)?) + value_to_iri(&uri)?.into() } Some(SubjectType::BlankNamed(subject)) => { // SPEC: §2.10 Identifying Blank Nodes: rdf:nodeID - graph - .borrow() - .statement_factory() - .blank_subject_named(subject) - .unwrap() + BlankNode::from_str(subject)?.into() } }; if !description_element.matches(name) { // SPEC: §2.13 Typed Node Elements - let statement_factory = graph.borrow().statement_factory(); - let mut graph = graph.borrow_mut(); - graph.insert( - statement_factory - .statement( - subject_node.clone(), - rdf::a_type().clone(), - statement_factory.named_object(name_to_iri(name)?), - ) - .unwrap(), - ); + graph.insert(Statement::new( + subject_node.clone(), + rdf::a_type().clone(), + name_to_iri(name)?, + )); } parse_predicate_attributes(&attributes.inner, &subject_node, graph)?; parse_predicate_element( @@ -264,23 +244,23 @@ fn parse_subject_element( } #[inline] -fn name_to_iri(name: &OwnedName) -> Result { - Ok(IriRef::new(Iri::from_str(&format!( +fn name_to_iri(name: &OwnedName) -> Result { + Ok(Iri::from_str(&format!( "{}{}", name.namespace.as_ref().unwrap(), name.local_name - ))?)) + ))?) } #[inline] -fn value_to_iri(name: &str) -> Result { - Ok(IriRef::new(Iri::from_str(name)?)) +fn value_to_iri(name: &str) -> Result { + Ok(Iri::from_str(name)?) } fn parse_predicate_attributes( attributes: &[&OwnedAttribute], - subject: &SubjectNodeRef, - graph: &mut GraphRef, + subject: &SubjectNode, + graph: &mut Graph, ) -> Result<(), Error> { // SPEC: §2.5 Property Attributes // SPEC: §2.12 Omitting Nodes: Property Attributes on an empty Property Element @@ -289,27 +269,20 @@ fn parse_predicate_attributes( "XmlReader::parse_predicate_attributes attribute: {:?}", attribute ); - let statement_factory = graph.borrow().statement_factory(); - let literal_factory = graph.borrow().literal_factory(); - let mut graph = graph.borrow_mut(); - graph.insert( - statement_factory - .statement( - subject.clone(), - name_to_iri(&attribute.name)?, - statement_factory.literal_object(literal_factory.literal(&attribute.value)), - ) - .unwrap(), - ); + graph.insert(Statement::new( + subject.clone(), + name_to_iri(&attribute.name)?, + Literal::plain(&attribute.value), + )); } Ok(()) } fn parse_predicate_element( event_reader: &mut EventReader<&mut R>, - xml_base: &Option, - subject: &SubjectNodeRef, - graph: &mut GraphRef, + xml_base: &Option, + subject: &SubjectNode, + graph: &mut Graph, ) -> Result<(), Error> { let mut no_child_elements = false; loop { @@ -327,22 +300,14 @@ fn parse_predicate_element( let attributes = parse_attributes(attributes)?; if let Some(resource) = attributes.resource { // SPEC: §2.4 Empty Property Elements - let statement_factory = graph.borrow().statement_factory(); - let mut graph = graph.borrow_mut(); - graph.insert( - statement_factory - .statement( - subject.clone(), - name_to_iri(name)?, - statement_factory.named_object(resource), - ) - .unwrap(), - ); + graph.insert(Statement::new( + subject.clone(), + name_to_iri(name)?, + resource, + )); // set outer loop value no_child_elements = true; } else { - let statement_factory = graph.borrow().statement_factory(); - let literal_factory = graph.borrow().literal_factory(); match attributes.parse_type { None => { if let Some(content) = @@ -350,23 +315,18 @@ fn parse_predicate_element( { let literal = if let Some(data_type) = attributes.data_type { // SPEC: §2.9 Typed Literals: rdf:datatype - literal_factory - .with_data_type(&content, DataType::from(data_type)) + Literal::with_data_type(&content, DataType::from(data_type)) } else if let Some(language) = attributes.language { // SPEC: §2.7 Languages: xml:lang - literal_factory.with_language(&content, language) + Literal::with_language(&content, language) } else { - literal_factory.literal(&content) + Literal::plain(&content) }; - graph.borrow_mut().insert( - statement_factory - .statement( - subject.clone(), - name_to_iri(name)?, - statement_factory.literal_object(literal), - ) - .unwrap(), - ); + graph.insert(Statement::new( + subject.clone(), + name_to_iri(name)?, + literal, + )); } } Some(ParseType::XmlLiteral) => { @@ -374,22 +334,15 @@ fn parse_predicate_element( let content = parse_xml_literal_element(event_reader)? .replace('<', "<") .replace('>', ">"); - graph.borrow_mut().insert( - statement_factory - .statement( - subject.clone(), - name_to_iri(name)?, - statement_factory.literal_object( - literal_factory - .with_data_type(&content, DataType::XmlLiteral), - ), - ) - .unwrap(), - ); + graph.insert(Statement::new( + subject.clone(), + name_to_iri(name)?, + Literal::with_data_type(&content, DataType::XmlLiteral), + )); } Some(ParseType::Resource) => { // SPEC: §2.11 Omitting Blank Nodes: rdf:parseType="Resource" - let subject_node = statement_factory.blank_subject_new(); + let subject_node = BlankNode::generate().into(); //parse_predicate_attributes(&attributes.inner, &subject_node, graph)?; let _subject = parse_subject_element( event_reader, @@ -425,8 +378,8 @@ fn parse_predicate_element( fn parse_object_element( event_reader: &mut EventReader<&mut R>, - xml_base: &Option, - graph: &mut GraphRef, + xml_base: &Option, + graph: &mut Graph, ) -> Result, Error> { let mut content = String::new(); let mut has_elements = false; @@ -446,7 +399,7 @@ fn parse_object_element( // set outer loop value has_elements = true; let attributes = parse_attributes(attributes)?; - let subject_node = graph.borrow().statement_factory().blank_subject_new(); + let subject_node = BlankNode::generate().into(); let _subject = parse_subject_element( event_reader, if attributes.uri_base.is_some() { diff --git a/rdftk_io/src/xml/writer.rs b/rdftk_io/src/xml/writer.rs index 32ac01e..b8a1ccd 100644 --- a/rdftk_io/src/xml/writer.rs +++ b/rdftk_io/src/xml/writer.rs @@ -5,11 +5,10 @@ use super::syntax::{ use crate::xml::syntax::ATTRIBUTE_XML_LANG_PREFIXED; use objio::{impl_has_options, ObjectWriter}; use rdftk_core::error::{rdf_star_not_supported_error, Error}; -use rdftk_core::model::graph::{Graph, GraphRef}; -use rdftk_core::model::statement::SubjectNodeRef; -use rdftk_iri::IriRef; +use rdftk_core::model::graph::Graph; +use rdftk_core::model::statement::SubjectNode; +use rdftk_iri::Iri; use rdftk_names::{dc, foaf, geo, owl, rdf, rdfs, xsd}; -use std::cell::Ref; use std::collections::HashMap; use std::io::Write; use xml::common::XmlVersion; @@ -134,15 +133,13 @@ impl Default for XmlWriter { impl_has_options!(XmlWriter, XmlOptions); -impl ObjectWriter for XmlWriter { +impl ObjectWriter for XmlWriter { type Error = Error; - fn write(&self, w: &mut W, graph: &GraphRef) -> Result<(), Self::Error> + fn write(&self, w: &mut W, graph: &Graph) -> Result<(), Self::Error> where W: Write, { - let graph = graph.borrow(); - let config = EmitterConfig::new() .perform_indent(self.options.pretty_print) .normalize_empty_elements(self.options.pretty_print); @@ -166,11 +163,11 @@ impl ObjectWriter for XmlWriter { if self.options.style == XmlStyle::Flat { for subject in graph.subjects() { - self.write_subject(&mut writer, &graph, subject, true)?; + self.write_subject(&mut writer, graph, subject, true)?; } } else { - for subject in graph.subjects().iter().filter(|s| s.is_iri()) { - self.write_subject(&mut writer, &graph, subject, false)?; + for subject in graph.subjects().iter().filter(|s| s.is_resource()) { + self.write_subject(&mut writer, graph, subject, false)?; } } @@ -231,8 +228,8 @@ impl XmlWriter { fn write_subject( &self, writer: &mut EventWriter, - graph: &Ref<'_, dyn Graph>, - subject: &SubjectNodeRef, + graph: &Graph, + subject: &SubjectNode, flat: bool, ) -> Result<(), Error> { if let Some(blank) = subject.as_blank() { @@ -248,11 +245,11 @@ impl XmlWriter { .write(XmlEvent::start_element(RDF_DESCRIPTION.as_str())) .map_err(xml_error)?; } - } else if let Some(subject) = subject.as_iri() { + } else if let Some(subject) = subject.as_resource() { writer .write( XmlEvent::start_element(RDF_DESCRIPTION.as_str()) - .attr(RDF_ABOUT.as_str(), &subject.to_string()), + .attr(RDF_ABOUT.as_str(), subject.as_ref()), ) .map_err(xml_error)?; } else { @@ -270,7 +267,7 @@ impl XmlWriter { XmlEvent::start_element(name.as_str()).default_ns(&ns) }; - if let Some(iri) = object.as_iri() { + if let Some(iri) = object.as_resource() { let iri = iri.to_string(); element = element.attr(RDF_RESOURCE.as_str(), &iri); writer.write(element).map_err(xml_error)?; @@ -280,12 +277,7 @@ impl XmlWriter { writer.write(element).map_err(xml_error)?; } else { writer.write(element).map_err(xml_error)?; - self.write_subject( - writer, - graph, - &graph.statement_factory().blank_subject(blank.clone()), - flat, - )?; + self.write_subject(writer, graph, &blank.clone().into(), flat)?; } } else if let Some(literal) = object.as_literal() { let language = literal @@ -335,7 +327,7 @@ fn xml_error(e: xml::writer::Error) -> Error { } } -fn split_uri(iri: &IriRef) -> (String, String) { +fn split_uri(iri: &Iri) -> (String, String) { let iri = iri.to_string(); let index = iri .chars() diff --git a/rdftk_io/tests/common/mod.rs b/rdftk_io/tests/common/mod.rs index 7e74676..832946f 100644 --- a/rdftk_io/tests/common/mod.rs +++ b/rdftk_io/tests/common/mod.rs @@ -1,11 +1,9 @@ -use rdftk_core::model::graph::{named::GraphName, GraphRef}; -use rdftk_core::model::graph::{NamedGraphRef, PrefixMappingRef}; -use rdftk_core::model::statement::StatementList; -use rdftk_core::simple::graph::graph_factory; -use rdftk_core::simple::literal::literal_factory; -use rdftk_core::simple::mapping::default_mappings; -use rdftk_core::simple::statement::statement_factory; -use rdftk_iri::{Iri, IriRef, Name}; +use rdftk_core::model::{ + graph::{Graph, GraphName, PrefixMapping}, + literal::Literal, + statement::{BlankNode, Statement}, +}; +use rdftk_iri::{Iri, Name}; use std::str::FromStr; #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] @@ -18,103 +16,64 @@ pub enum TonyBennType { } #[allow(dead_code)] -pub fn tony_benn_named_graph(graph_type: TonyBennType) -> NamedGraphRef { +pub fn tony_benn_named_graph(graph_type: TonyBennType) -> Graph { let name = GraphName::from(Iri::from_str("http://en.wikipedia.org/wiki/Tony_Benn").unwrap()); let (mappings, statements) = some_tony_benn_graph(graph_type); - graph_factory().named_graph_from(Some(name.into()), &statements, Some(mappings)) + Graph::named(name) + .with_mappings(mappings) + .with_statements(statements) } #[allow(dead_code)] -pub fn tony_benn_graph(graph_type: TonyBennType) -> GraphRef { +pub fn tony_benn_graph(graph_type: TonyBennType) -> Graph { let (mappings, statements) = some_tony_benn_graph(graph_type); - graph_factory().graph_from(&statements, Some(mappings)) + Graph::default() + .with_mappings(mappings) + .with_statements(statements) } -fn some_tony_benn_graph(graph_type: TonyBennType) -> (PrefixMappingRef, StatementList) { - let mappings = default_mappings(); - { - let mut mut_mappings = mappings.borrow_mut(); - mut_mappings.insert_rdf(); - mut_mappings.insert( - Name::new_unchecked("dc"), - IriRef::from(Iri::from_str("http://purl.org/dc/elements/1.1/").unwrap()), - ); - mut_mappings.insert( - Name::new_unchecked("foaf"), - IriRef::from(Iri::from_str("http://xmlns.com/foaf/0.1/").unwrap()), +fn some_tony_benn_graph(graph_type: TonyBennType) -> (PrefixMapping, Vec) { + let mut mappings = PrefixMapping::default() + .with_rdf() + .with_dc_elements() + .with_foaf(); + if graph_type == TonyBennType::TwoTypes { + mappings.insert( + Name::new_unchecked("fibo-fnd-aap-ppl"), + Iri::from_str("https://spec.edmcouncil.org/fibo/ontology/FND/AgentsAndPeople/People/") + .unwrap(), ); - if graph_type == TonyBennType::TwoTypes { - mut_mappings.insert( - Name::new_unchecked("fibo-fnd-aap-ppl"), - IriRef::from( - Iri::from_str( - "https://spec.edmcouncil.org/fibo/ontology/FND/AgentsAndPeople/People/", - ) - .unwrap(), - ), - ); - } } - let st_factory = statement_factory(); - let lit_factory = literal_factory(); - - let mut statements: StatementList = Default::default(); - - let subject_iri = - IriRef::from(Iri::from_str("http://en.wikipedia.org/wiki/Tony_Benn").unwrap()); - - statements.push( - st_factory - .statement( - st_factory.named_subject(subject_iri.clone()), - IriRef::from(Iri::from_str("http://purl.org/dc/elements/1.1/title").unwrap()), - st_factory.literal_object(lit_factory.literal("Tony Benn")), - ) - .unwrap(), - ); - statements.push( - st_factory - .statement( - st_factory.named_subject(subject_iri.clone()), - IriRef::from(Iri::from_str("http://purl.org/dc/elements/1.1/publisher").unwrap()), - st_factory.literal_object(lit_factory.literal("Wikipedia")), - ) - .unwrap(), - ); - statements.push( - st_factory - .statement( - st_factory.named_subject(subject_iri), - IriRef::from(Iri::from_str("http://purl.org/dc/elements/1.1/description").unwrap()), - st_factory.blank_object_named("B1").unwrap(), - ) - .unwrap(), - ); - statements.push( - st_factory - .statement( - st_factory.blank_subject_named("B1").unwrap(), - IriRef::from(Iri::from_str("http://xmlns.com/foaf/0.1/name").unwrap()), - st_factory.literal_object(lit_factory.literal("Tony Benn")), - ) - .unwrap(), - ); - statements.push( - st_factory - .statement( - st_factory.blank_subject_named("B1").unwrap(), - IriRef::from( - Iri::from_str("http://www.w3.org/1999/02/22-rdf-syntax-ns#type").unwrap(), - ), - st_factory.named_object( - Iri::from_str("http://xmlns.com/foaf/0.1/Person") - .unwrap() - .into(), - ), - ) - .unwrap(), - ); + let mut statements = Vec::default(); + + let subject_iri = Iri::from_str("http://en.wikipedia.org/wiki/Tony_Benn").unwrap(); + + statements.push(Statement::new( + subject_iri.clone(), + Iri::from_str("http://purl.org/dc/elements/1.1/title").unwrap(), + Literal::plain("Tony Benn"), + )); + statements.push(Statement::new( + subject_iri.clone(), + Iri::from_str("http://purl.org/dc/elements/1.1/publisher").unwrap(), + Literal::plain("Wikipedia"), + )); + statements.push(Statement::new( + subject_iri, + Iri::from_str("http://purl.org/dc/elements/1.1/description").unwrap(), + BlankNode::from_str("B1").unwrap(), + )); + statements.push(Statement::new( + BlankNode::from_str("B1").unwrap(), + Iri::from_str("http://xmlns.com/foaf/0.1/name").unwrap(), + Literal::plain("Tony Benn"), + )); + statements.push(Statement::new( + BlankNode::from_str("B1").unwrap(), + Iri::from_str("http://www.w3.org/1999/02/22-rdf-syntax-ns#type").unwrap(), + Iri::from_str("http://xmlns.com/foaf/0.1/Person").unwrap(), + )); (mappings, statements) } @@ -137,26 +96,21 @@ fn some_tony_benn_graph(graph_type: TonyBennType) -> (PrefixMappingRef, Statemen /// . /// ``` #[allow(dead_code)] -pub fn use_cases_graph() -> GraphRef { - let mappings = default_mappings(); - { - let mut mut_mappings = mappings.borrow_mut(); - mut_mappings.insert_rdf(); - mut_mappings.insert( +pub fn use_cases_graph() -> Graph { + let mappings = PrefixMapping::default() + .with_rdf() + .with( Name::new_unchecked("use-case"), - IriRef::from(Iri::from_str("https://ekgf.org/ontology/use-case/").unwrap()), - ); - mut_mappings.insert( + Iri::from_str("https://ekgf.org/ontology/use-case/").unwrap(), + ) + .with( Name::new_unchecked("test"), - IriRef::from(Iri::from_str("https://whatever.org/ontology/test/").unwrap()), + Iri::from_str("https://whatever.org/ontology/test/").unwrap(), ); - } - let st_factory = statement_factory(); - let mut statements: StatementList = Default::default(); + let mut statements = Vec::default(); - let subject_iri = - IriRef::from(Iri::from_str("https://placeholder.kg/id/use-case-currencies").unwrap()); + let subject_iri = Iri::from_str("https://placeholder.kg/id/use-case-currencies").unwrap(); for concept_iri in [ "functional-currency", @@ -167,39 +121,24 @@ pub fn use_cases_graph() -> GraphRef { "functional-currency-label", "share-issue-denomination-currency", ] - .map(|c| { - IriRef::from( - Iri::from_str(format!("https://placeholder.kg/id/concept-{c}").as_str()).unwrap(), - ) - }) { - statements.push( - st_factory - .statement( - st_factory.named_subject(subject_iri.clone()), - IriRef::from( - Iri::from_str("https://ekgf.org/ontology/use-case/usesConcept").unwrap(), - ), - st_factory.named_object(concept_iri), - ) - .unwrap(), - ); + .map(|c| Iri::from_str(format!("https://placeholder.kg/id/concept-{c}").as_str()).unwrap()) + { + statements.push(Statement::new( + subject_iri.clone(), + Iri::from_str("https://ekgf.org/ontology/use-case/usesConcept").unwrap(), + concept_iri, + )); } - statements.push( - st_factory - .statement( - st_factory.named_subject(subject_iri.clone()), - IriRef::from( - Iri::from_str("https://whatever.org/ontology/test/predicate").unwrap(), - ), - st_factory.named_object(IriRef::from( - Iri::from_str("https://whatever.org/ontology/test/whatever").unwrap(), - )), - ) - .unwrap(), - ); + statements.push(Statement::new( + subject_iri, + Iri::from_str("https://whatever.org/ontology/test/predicate").unwrap(), + Iri::from_str("https://whatever.org/ontology/test/whatever").unwrap(), + )); - graph_factory().graph_from(&statements, Some(mappings)) + Graph::default() + .with_mappings(mappings) + .with_statements(statements) } /// @@ -229,35 +168,28 @@ pub fn use_cases_graph() -> GraphRef { /// ] . /// ``` #[allow(dead_code)] -pub fn many_blank_nodes_graph() -> GraphRef { - let mappings = default_mappings(); - { - let mut mut_mappings = mappings.borrow_mut(); - mut_mappings.insert_rdf(); - mut_mappings.insert_rdfs(); - mut_mappings.insert( +pub fn many_blank_nodes_graph() -> Graph { + let mappings = PrefixMapping::default() + .with_rdf() + .with_rdfs() + .with( Name::new_unchecked("use-case"), - IriRef::from(Iri::from_str("https://ekgf.org/ontology/use-case/").unwrap()), - ); - mut_mappings.insert( + Iri::from_str("https://ekgf.org/ontology/use-case/").unwrap(), + ) + .with( Name::new_unchecked("concept"), - IriRef::from(Iri::from_str("https://ekgf.org/ontology/concept/").unwrap()), - ); - mut_mappings.insert( + Iri::from_str("https://ekgf.org/ontology/concept/").unwrap(), + ) + .with( Name::new_unchecked("graph"), - IriRef::from(Iri::from_str("https://yourcompany.com/graph/").unwrap()), + Iri::from_str("https://yourcompany.com/graph/").unwrap(), ); - } - let st_factory = statement_factory(); - let li_factory = literal_factory(); - let mut statements: StatementList = Default::default(); + let mut statements = Vec::default(); - let subject_iri = - IriRef::from(Iri::from_str("https://yourcompany.com/id/use-case-currencies").unwrap()); + let subject_iri = Iri::from_str("https://yourcompany.com/id/use-case-currencies").unwrap(); - let concept_type_iri = - IriRef::from(Iri::from_str("https://ekgf.org/ontology/concept/Concept").unwrap()); + let concept_type_iri = Iri::from_str("https://ekgf.org/ontology/concept/Concept").unwrap(); for concept_label in [ "Capital Raise Currency", @@ -265,41 +197,25 @@ pub fn many_blank_nodes_graph() -> GraphRef { "Currency Search Text", "Currency Tag", ] { - let bn = st_factory.blank_object_new(); - statements.push( - st_factory - .statement( - st_factory.named_subject(subject_iri.clone()), - IriRef::from( - Iri::from_str("https://ekgf.org/ontology/use-case/usesConcept").unwrap(), - ), - bn.clone(), - ) - .unwrap(), - ); - statements.push( - st_factory - .statement( - st_factory.object_as_subject(bn.clone()).unwrap(), - IriRef::from( - Iri::from_str("http://www.w3.org/1999/02/22-rdf-syntax-ns#type").unwrap(), - ), - st_factory.named_object(concept_type_iri.clone()), - ) - .unwrap(), - ); - statements.push( - st_factory - .statement( - st_factory.object_as_subject(bn.clone()).unwrap(), - IriRef::from( - Iri::from_str("http://www.w3.org/2000/01/rdf-schema#label").unwrap(), - ), - st_factory.literal_object(li_factory.literal(concept_label)), - ) - .unwrap(), - ); + let bn = BlankNode::generate(); + statements.push(Statement::new( + subject_iri.clone(), + Iri::from_str("https://ekgf.org/ontology/use-case/usesConcept").unwrap(), + bn.clone(), + )); + statements.push(Statement::new( + bn.clone(), + Iri::from_str("http://www.w3.org/1999/02/22-rdf-syntax-ns#type").unwrap(), + concept_type_iri.clone(), + )); + statements.push(Statement::new( + bn, + Iri::from_str("http://www.w3.org/2000/01/rdf-schema#label").unwrap(), + Literal::plain(concept_label), + )); } - graph_factory().graph_from(&statements, Some(mappings)) + Graph::default() + .with_mappings(mappings) + .with_statements(statements) } diff --git a/rdftk_io/tests/read_json.rs b/rdftk_io/tests/read_json.rs index ed7a3ab..01bad59 100644 --- a/rdftk_io/tests/read_json.rs +++ b/rdftk_io/tests/read_json.rs @@ -19,7 +19,7 @@ fn read_example_01() { assert!(result.is_ok()); let graph = result.unwrap(); println!("{:?}", graph); - assert_eq!(graph.borrow().len(), 1); + assert_eq!(graph.len(), 1); } #[test] @@ -41,7 +41,7 @@ fn read_example_02() { assert!(result.is_ok()); let graph = result.unwrap(); println!("{:?}", graph); - assert_eq!(graph.borrow().len(), 2); + assert_eq!(graph.len(), 2); } #[test] @@ -61,5 +61,5 @@ fn read_example_03() { assert!(result.is_ok()); let graph = result.unwrap(); println!("{:?}", graph); - assert_eq!(graph.borrow().len(), 2); + assert_eq!(graph.len(), 2); } diff --git a/rdftk_io/tests/read_nt.rs b/rdftk_io/tests/read_nt.rs index 04d47e6..02bb1aa 100644 --- a/rdftk_io/tests/read_nt.rs +++ b/rdftk_io/tests/read_nt.rs @@ -1,10 +1,10 @@ #![cfg(feature = "nt")] use objio::{ObjectReader, ObjectWriter}; -use rdftk_core::{error::Error, model::graph::GraphRef}; +use rdftk_core::{error::Error, model::graph::Graph}; use rdftk_io::nt::{NTripleReader, NTripleWriter}; -fn write_graph(graph: &GraphRef) { +fn write_graph(graph: &Graph) { let writer = NTripleWriter::default(); let _ = writer.write(&mut std::io::stdout(), graph); } @@ -12,7 +12,7 @@ fn write_graph(graph: &GraphRef) { #[test] fn parse_simple() { let reader = NTripleReader::default(); - let result: Result = reader.read_from_string( + let result: Result = reader.read_from_string( r###" "That Seventies Show"^^ . # literal with XML Schema string datatype "That Seventies Show" . # same as above @@ -37,7 +37,7 @@ fn parse_simple() { #[test] fn parse_simple_with_blanks() { let reader = NTripleReader::default(); - let result: Result = reader.read_from_string( + let result: Result = reader.read_from_string( r###" . # comments here # or on a line by themselves diff --git a/rdftk_io/tests/read_turtle.rs b/rdftk_io/tests/read_turtle.rs index c68ffb8..00be885 100644 --- a/rdftk_io/tests/read_turtle.rs +++ b/rdftk_io/tests/read_turtle.rs @@ -1,14 +1,14 @@ #![cfg(feature = "turtle")] use objio::ObjectReader; -use rdftk_core::{error::Error, model::graph::GraphRef}; +use rdftk_core::{error::Error, model::graph::Graph}; use rdftk_io::turtle::TurtleReader; #[test] #[ignore] fn parse_simple_turtle() { let reader = TurtleReader::default(); - let result: Result = reader.read_from_string( + let result: Result = reader.read_from_string( r###"@base . @prefix rdf: . @prefix rdfs: . diff --git a/rdftk_io/tests/read_xml.rs b/rdftk_io/tests/read_xml.rs index 3158f2f..d3c77f4 100644 --- a/rdftk_io/tests/read_xml.rs +++ b/rdftk_io/tests/read_xml.rs @@ -24,7 +24,7 @@ fn read_example_empty_graph() { assert!(result.is_ok()); let graph = result.unwrap(); println!("{:?}", graph); - assert_eq!(graph.borrow().len(), 0); + assert_eq!(graph.len(), 0); } #[test] @@ -55,8 +55,8 @@ fn read_example_01() { let result = reader.read(&mut xml); assert!(result.is_ok()); let graph = result.unwrap(); - for st in graph.borrow().statements() { + for st in graph.statements() { println!("{}", st); } - assert_eq!(graph.borrow().len(), 6); + assert_eq!(graph.len(), 6); } diff --git a/rdftk_io/tests/w3c_nt.rs b/rdftk_io/tests/w3c_nt.rs index d714750..b868abf 100644 --- a/rdftk_io/tests/w3c_nt.rs +++ b/rdftk_io/tests/w3c_nt.rs @@ -2,7 +2,7 @@ use objio::ObjectReader; use rdftk_core::error::eprint_trace; -use rdftk_core::model::graph::GraphRef; +use rdftk_core::model::graph::Graph; use rdftk_io::nt::NTripleReader; use std::path::PathBuf; @@ -32,7 +32,7 @@ macro_rules! negative_test { }; } -fn read_test_file(file_name: &str) -> Result { +fn read_test_file(file_name: &str) -> Result { let file_path = PathBuf::from(format!("tests/w3c/nt/{}.nt", file_name)); let reader = NTripleReader::default(); reader.read_from_file(file_path) diff --git a/rdftk_io/tests/write_nq.rs b/rdftk_io/tests/write_nq.rs index 3e9c5c8..d6dad4f 100644 --- a/rdftk_io/tests/write_nq.rs +++ b/rdftk_io/tests/write_nq.rs @@ -1,8 +1,7 @@ #![cfg(feature = "nq")] use objio::ObjectWriter; -use rdftk_core::model::data_set::DataSetRef; -use rdftk_core::simple::data_set::data_set_factory; +use rdftk_core::model::data_set::DataSet; use rdftk_io::nq::NQuadWriter; mod common; @@ -10,15 +9,11 @@ mod common; #[test] fn write_to_nquads() { let graph = common::tony_benn_named_graph(common::TonyBennType::OneType); - let data_set = data_set_factory().data_set(); - { - let mut data_set = data_set.borrow_mut(); - data_set.insert(graph); - } + let data_set = DataSet::from(graph); let writer = NQuadWriter::default(); - let result = writer.write_to_string(&(data_set as DataSetRef)); + let result = writer.write_to_string(&data_set); assert!(result.is_ok()); let output = result.unwrap(); println!("# format: N-Quads\n{}", output); diff --git a/rdftk_io/tests/write_turtle.rs b/rdftk_io/tests/write_turtle.rs index 4af485a..cd40002 100644 --- a/rdftk_io/tests/write_turtle.rs +++ b/rdftk_io/tests/write_turtle.rs @@ -1,8 +1,8 @@ #![cfg(feature = "turtle")] use objio::{HasOptions, ObjectWriter}; -use rdftk_io::turtle::{TurtleOptions, TurtleWriter}; -use rdftk_iri::{Iri, IriRef}; +use rdftk_io::turtle::{TurtleWriter, TurtleWriterOptions}; +use rdftk_iri::Iri; use std::str::FromStr; mod common; @@ -31,9 +31,8 @@ fn write_to_turtle() { fn write_to_turtle_with_base() { let graph = common::tony_benn_graph(Default::default()); - let options = TurtleOptions::default().with_id_base(IriRef::from( - Iri::from_str("http://en.wikipedia.org/wiki/").unwrap(), - )); + let options = TurtleWriterOptions::default() + .with_id_base(Iri::from_str("http://en.wikipedia.org/wiki/").unwrap()); let writer = TurtleWriter::default().with_options(options); let result = writer.write_to_string(&graph); @@ -55,10 +54,8 @@ fn write_to_turtle_with_base() { fn write_to_turtle_with_options() { let graph = common::tony_benn_graph(Default::default()); - let options = TurtleOptions::default() - .with_id_base(IriRef::from( - Iri::from_str("http://en.wikipedia.org/wiki/").unwrap(), - )) + let options = TurtleWriterOptions::default() + .with_id_base(Iri::from_str("http://en.wikipedia.org/wiki/").unwrap()) .with_sparql_style() .with_nested_blank_nodes(); let writer = TurtleWriter::default().with_options(options);