use crate::model::{BlankNode, GraphName, Literal, NamedNode, Object, Quad, Subject, Triple};
use crate::{OxirsError, Result};
use oxiri::Iri;
pub struct DataFactory;
impl DataFactory {
pub fn named_node(iri: impl Into<String>) -> Result<NamedNode> {
NamedNode::new(iri)
}
pub fn blank_node() -> BlankNode {
BlankNode::new_unique()
}
pub fn blank_node_with_id(id: impl Into<String>) -> BlankNode {
let s = id.into();
BlankNode::new(s.clone()).unwrap_or_else(|_| BlankNode::new_unique())
}
pub fn literal(value: impl Into<String>) -> Literal {
Literal::new(value)
}
pub fn typed_literal(value: impl Into<String>, datatype: NamedNode) -> Literal {
Literal::new_typed(value, datatype)
}
pub fn language_literal(value: impl Into<String>, lang: impl Into<String>) -> Result<Literal> {
let lang_str = lang.into();
Self::validate_lang_tag(&lang_str)?;
Literal::new_lang(value, lang_str)
}
pub fn triple(subject: Subject, predicate: NamedNode, object: Object) -> Triple {
Triple::new(subject, predicate, object)
}
pub fn quad(subject: Subject, predicate: NamedNode, object: Object, graph: GraphName) -> Quad {
Quad::new(subject, predicate, object, graph)
}
pub fn default_graph() -> GraphName {
GraphName::DefaultGraph
}
pub fn named_graph(iri: impl Into<String>) -> Result<GraphName> {
let nn = NamedNode::new(iri)?;
Ok(GraphName::NamedNode(nn))
}
pub fn validate_iri(iri: &str) -> Result<()> {
Iri::parse(iri.to_owned())
.map(|_| ())
.map_err(|e| OxirsError::Parse(format!("Invalid IRI '{iri}': {e}")))
}
pub fn validate_lang_tag(lang: &str) -> Result<()> {
if lang.is_empty() {
return Err(OxirsError::Parse(
"Language tag must not be empty".to_string(),
));
}
for part in lang.split('-') {
if part.is_empty() {
return Err(OxirsError::Parse(format!(
"Invalid language tag '{lang}': empty subtag"
)));
}
if !part.chars().all(|c| c.is_ascii_alphanumeric()) {
return Err(OxirsError::Parse(format!(
"Invalid language tag '{lang}': subtag '{part}' contains non-alphanumeric characters"
)));
}
}
let primary = lang.split('-').next().unwrap_or(lang);
if !primary.chars().all(|c| c.is_ascii_alphabetic()) {
return Err(OxirsError::Parse(format!(
"Invalid language tag '{lang}': primary subtag must be alphabetic"
)));
}
Ok(())
}
}
pub mod xsd_types {
use crate::model::NamedNode;
const XSD: &str = "http://www.w3.org/2001/XMLSchema#";
pub fn string() -> NamedNode {
NamedNode::new_unchecked(format!("{XSD}string"))
}
pub fn integer() -> NamedNode {
NamedNode::new_unchecked(format!("{XSD}integer"))
}
pub fn float() -> NamedNode {
NamedNode::new_unchecked(format!("{XSD}float"))
}
pub fn double() -> NamedNode {
NamedNode::new_unchecked(format!("{XSD}double"))
}
pub fn boolean() -> NamedNode {
NamedNode::new_unchecked(format!("{XSD}boolean"))
}
pub fn date_time() -> NamedNode {
NamedNode::new_unchecked(format!("{XSD}dateTime"))
}
pub fn date() -> NamedNode {
NamedNode::new_unchecked(format!("{XSD}date"))
}
pub fn decimal() -> NamedNode {
NamedNode::new_unchecked(format!("{XSD}decimal"))
}
pub fn long() -> NamedNode {
NamedNode::new_unchecked(format!("{XSD}long"))
}
pub fn int() -> NamedNode {
NamedNode::new_unchecked(format!("{XSD}int"))
}
pub fn short() -> NamedNode {
NamedNode::new_unchecked(format!("{XSD}short"))
}
pub fn byte() -> NamedNode {
NamedNode::new_unchecked(format!("{XSD}byte"))
}
pub fn unsigned_long() -> NamedNode {
NamedNode::new_unchecked(format!("{XSD}unsignedLong"))
}
pub fn unsigned_int() -> NamedNode {
NamedNode::new_unchecked(format!("{XSD}unsignedInt"))
}
pub fn non_negative_integer() -> NamedNode {
NamedNode::new_unchecked(format!("{XSD}nonNegativeInteger"))
}
pub fn positive_integer() -> NamedNode {
NamedNode::new_unchecked(format!("{XSD}positiveInteger"))
}
pub fn any_uri() -> NamedNode {
NamedNode::new_unchecked(format!("{XSD}anyURI"))
}
pub fn base64_binary() -> NamedNode {
NamedNode::new_unchecked(format!("{XSD}base64Binary"))
}
pub fn hex_binary() -> NamedNode {
NamedNode::new_unchecked(format!("{XSD}hexBinary"))
}
pub fn g_year() -> NamedNode {
NamedNode::new_unchecked(format!("{XSD}gYear"))
}
pub fn duration() -> NamedNode {
NamedNode::new_unchecked(format!("{XSD}duration"))
}
pub fn time() -> NamedNode {
NamedNode::new_unchecked(format!("{XSD}time"))
}
pub fn normalized_string() -> NamedNode {
NamedNode::new_unchecked(format!("{XSD}normalizedString"))
}
pub fn token() -> NamedNode {
NamedNode::new_unchecked(format!("{XSD}token"))
}
}
pub mod vocab {
use crate::model::NamedNode;
pub mod rdf {
use super::NamedNode;
const NS: &str = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
pub fn r#type() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}type"))
}
pub fn subject() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}subject"))
}
pub fn predicate() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}predicate"))
}
pub fn object() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}object"))
}
pub fn property() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}Property"))
}
pub fn statement() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}Statement"))
}
pub fn first() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}first"))
}
pub fn rest() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}rest"))
}
pub fn nil() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}nil"))
}
pub fn list() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}List"))
}
pub fn bag() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}Bag"))
}
pub fn seq() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}Seq"))
}
pub fn alt() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}Alt"))
}
pub fn value() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}value"))
}
pub fn lang_string() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}langString"))
}
pub const NAMESPACE: &str = NS;
}
pub mod rdfs {
use super::NamedNode;
const NS: &str = "http://www.w3.org/2000/01/rdf-schema#";
pub fn label() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}label"))
}
pub fn comment() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}comment"))
}
pub fn sub_class_of() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}subClassOf"))
}
pub fn sub_property_of() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}subPropertyOf"))
}
pub fn domain() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}domain"))
}
pub fn range() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}range"))
}
pub fn class() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}Class"))
}
pub fn resource() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}Resource"))
}
pub fn literal() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}Literal"))
}
pub fn datatype() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}Datatype"))
}
pub fn is_defined_by() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}isDefinedBy"))
}
pub fn see_also() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}seeAlso"))
}
pub fn member() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}member"))
}
pub fn container() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}Container"))
}
pub const NAMESPACE: &str = NS;
}
pub mod owl {
use super::NamedNode;
const NS: &str = "http://www.w3.org/2002/07/owl#";
pub fn class() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}Class"))
}
pub fn object_property() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}ObjectProperty"))
}
pub fn datatype_property() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}DatatypeProperty"))
}
pub fn annotation_property() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}AnnotationProperty"))
}
pub fn thing() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}Thing"))
}
pub fn nothing() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}Nothing"))
}
pub fn same_as() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}sameAs"))
}
pub fn equivalent_class() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}equivalentClass"))
}
pub fn equivalent_property() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}equivalentProperty"))
}
pub fn inverse_of() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}inverseOf"))
}
pub fn disjoint_with() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}disjointWith"))
}
pub fn functional_property() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}FunctionalProperty"))
}
pub fn ontology() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}Ontology"))
}
pub const NAMESPACE: &str = NS;
}
pub mod xsd {
use super::NamedNode;
const NS: &str = "http://www.w3.org/2001/XMLSchema#";
pub fn string() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}string"))
}
pub fn integer() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}integer"))
}
pub fn boolean() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}boolean"))
}
pub fn double() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}double"))
}
pub fn date_time() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}dateTime"))
}
pub const NAMESPACE: &str = NS;
}
pub mod foaf {
use super::NamedNode;
const NS: &str = "http://xmlns.com/foaf/0.1/";
pub fn name() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}name"))
}
pub fn person() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}Person"))
}
pub fn knows() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}knows"))
}
pub fn mbox() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}mbox"))
}
pub fn homepage() -> NamedNode {
NamedNode::new_unchecked(format!("{NS}homepage"))
}
pub const NAMESPACE: &str = NS;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_named_node_valid_http() {
let n = DataFactory::named_node("http://example.org/s").expect("valid IRI for named node");
assert_eq!(n.as_str(), "http://example.org/s");
}
#[test]
fn test_named_node_valid_https() {
let n =
DataFactory::named_node("https://schema.org/Person").expect("valid IRI for named node");
assert_eq!(n.as_str(), "https://schema.org/Person");
}
#[test]
fn test_named_node_valid_urn() {
let n = DataFactory::named_node("urn:example:foo").expect("valid IRI for named node");
assert_eq!(n.as_str(), "urn:example:foo");
}
#[test]
fn test_named_node_invalid_returns_err() {
assert!(DataFactory::named_node("not an IRI").is_err());
}
#[test]
fn test_named_node_empty_string_is_err() {
assert!(DataFactory::named_node("").is_err());
}
#[test]
fn test_named_node_with_fragment() {
let n = DataFactory::named_node("http://example.org/ont#Class")
.expect("valid IRI for named node");
assert!(n.as_str().ends_with("#Class"));
}
#[test]
fn test_named_node_with_query() {
let n =
DataFactory::named_node("http://example.org/q?a=1").expect("valid IRI for named node");
assert!(n.as_str().contains("a=1"));
}
#[test]
fn test_blank_node_auto_id_is_nonempty() {
let b = DataFactory::blank_node();
assert!(!b.as_str().is_empty());
}
#[test]
fn test_blank_node_auto_ids_are_unique() {
let b1 = DataFactory::blank_node();
let b2 = DataFactory::blank_node();
assert_ne!(b1.as_str(), b2.as_str());
}
#[test]
fn test_blank_node_with_id() {
let b = DataFactory::blank_node_with_id("my-node");
assert_eq!(b.as_str(), "my-node");
}
#[test]
fn test_blank_node_with_id_alpha() {
let b = DataFactory::blank_node_with_id("abc");
assert_eq!(b.as_str(), "abc");
}
#[test]
fn test_blank_node_with_id_alphanumeric() {
let b = DataFactory::blank_node_with_id("node1");
assert_eq!(b.as_str(), "node1");
}
#[test]
fn test_blank_node_with_invalid_id_still_returns_node() {
let b = DataFactory::blank_node_with_id(" spaces ");
assert!(!b.as_str().is_empty());
}
#[test]
fn test_literal_plain_value() {
let l = DataFactory::literal("hello");
assert_eq!(l.value(), "hello");
}
#[test]
fn test_literal_plain_no_language() {
let l = DataFactory::literal("hello");
assert_eq!(l.language(), None);
}
#[test]
fn test_literal_plain_datatype_is_xsd_string() {
let l = DataFactory::literal("hello");
assert!(l.datatype().as_str().contains("string"));
}
#[test]
fn test_literal_empty_string() {
let l = DataFactory::literal("");
assert_eq!(l.value(), "");
}
#[test]
fn test_typed_literal_integer() {
let l = DataFactory::typed_literal("42", xsd_types::integer());
assert_eq!(l.value(), "42");
assert!(l.datatype().as_str().ends_with("integer"));
}
#[test]
fn test_typed_literal_boolean() {
let l = DataFactory::typed_literal("true", xsd_types::boolean());
assert_eq!(l.value(), "true");
assert!(l.datatype().as_str().ends_with("boolean"));
}
#[test]
fn test_typed_literal_double() {
let l = DataFactory::typed_literal("3.14", xsd_types::double());
assert!(l.datatype().as_str().ends_with("double"));
}
#[test]
fn test_typed_literal_date_time() {
let l = DataFactory::typed_literal("2026-02-24T00:00:00Z", xsd_types::date_time());
assert!(l.datatype().as_str().ends_with("dateTime"));
}
#[test]
fn test_typed_literal_custom_datatype() {
let dt =
DataFactory::named_node("http://example.org/myType").expect("valid IRI for named node");
let l = DataFactory::typed_literal("custom", dt);
assert!(l.datatype().as_str().contains("myType"));
}
#[test]
fn test_language_literal_value_and_lang() {
let l = DataFactory::language_literal("Bonjour", "fr").expect("valid language literal");
assert_eq!(l.value(), "Bonjour");
assert_eq!(l.language(), Some("fr"));
}
#[test]
fn test_language_literal_en() {
let l = DataFactory::language_literal("Hello", "en").expect("valid language literal");
assert_eq!(l.language(), Some("en"));
}
#[test]
fn test_language_literal_zh_hans() {
let l = DataFactory::language_literal("你好", "zh-Hans").expect("valid language literal");
assert_eq!(l.language(), Some("zh-hans"));
}
#[test]
fn test_language_literal_en_us() {
let l = DataFactory::language_literal("Color", "en-US").expect("valid language literal");
assert_eq!(l.language(), Some("en-us"));
}
#[test]
fn test_language_literal_empty_lang_is_err() {
assert!(DataFactory::language_literal("hello", "").is_err());
}
#[test]
fn test_language_literal_invalid_lang_is_err() {
assert!(DataFactory::language_literal("hello", "en US").is_err());
}
#[test]
fn test_triple_subject_predicate_object() {
let s = DataFactory::named_node("http://example.org/s").expect("valid IRI for named node");
let p = DataFactory::named_node("http://example.org/p").expect("valid IRI for named node");
let o = DataFactory::named_node("http://example.org/o").expect("valid IRI for named node");
let t = DataFactory::triple(s.into(), p.clone(), o.into());
let text = format!("{t}");
assert!(text.contains("http://example.org/s"));
}
#[test]
fn test_triple_with_literal_object() {
let s = DataFactory::named_node("http://example.org/s").expect("valid IRI for named node");
let p = DataFactory::named_node("http://example.org/p").expect("valid IRI for named node");
let o: Object = DataFactory::literal("hello").into();
let t = DataFactory::triple(s.into(), p, o);
let text = format!("{t}");
assert!(text.contains("hello"));
}
#[test]
fn test_triple_with_blank_node_subject() {
let s: Subject = DataFactory::blank_node().into();
let p = DataFactory::named_node("http://example.org/p").expect("valid IRI for named node");
let o: Object = DataFactory::literal("val").into();
let _t = DataFactory::triple(s, p, o);
}
#[test]
fn test_quad_default_graph() {
let s = DataFactory::named_node("http://example.org/s").expect("valid IRI for named node");
let p = DataFactory::named_node("http://example.org/p").expect("valid IRI for named node");
let o = DataFactory::named_node("http://example.org/o").expect("valid IRI for named node");
let g = DataFactory::default_graph();
let q = DataFactory::quad(s.into(), p, o.into(), g);
let text = format!("{q}");
assert!(text.contains("http://example.org/s"));
}
#[test]
fn test_quad_named_graph() {
let s = DataFactory::named_node("http://example.org/s").expect("valid IRI for named node");
let p = DataFactory::named_node("http://example.org/p").expect("valid IRI for named node");
let o = DataFactory::named_node("http://example.org/o").expect("valid IRI for named node");
let g = DataFactory::named_graph("http://example.org/graph1")
.expect("construction should succeed");
let q = DataFactory::quad(s.into(), p, o.into(), g);
let text = format!("{q}");
assert!(text.contains("graph1"));
}
#[test]
fn test_quad_named_graph_invalid_iri_is_err() {
assert!(DataFactory::named_graph("not an IRI").is_err());
}
#[test]
fn test_default_graph_is_default_graph_variant() {
let g = DataFactory::default_graph();
assert!(matches!(g, GraphName::DefaultGraph));
}
#[test]
fn test_validate_iri_http_ok() {
assert!(DataFactory::validate_iri("http://example.org/").is_ok());
}
#[test]
fn test_validate_iri_https_ok() {
assert!(DataFactory::validate_iri("https://example.org/path").is_ok());
}
#[test]
fn test_validate_iri_urn_ok() {
assert!(DataFactory::validate_iri("urn:isbn:0451450523").is_ok());
}
#[test]
fn test_validate_iri_bare_word_is_err() {
assert!(DataFactory::validate_iri("hello").is_err());
}
#[test]
fn test_validate_iri_empty_is_err() {
assert!(DataFactory::validate_iri("").is_err());
}
#[test]
fn test_validate_iri_space_is_err() {
assert!(DataFactory::validate_iri("http://example.org/hello world").is_err());
}
#[test]
fn test_validate_lang_tag_en_ok() {
assert!(DataFactory::validate_lang_tag("en").is_ok());
}
#[test]
fn test_validate_lang_tag_en_us_ok() {
assert!(DataFactory::validate_lang_tag("en-US").is_ok());
}
#[test]
fn test_validate_lang_tag_zh_hans_cn_ok() {
assert!(DataFactory::validate_lang_tag("zh-Hans-CN").is_ok());
}
#[test]
fn test_validate_lang_tag_empty_is_err() {
assert!(DataFactory::validate_lang_tag("").is_err());
}
#[test]
fn test_validate_lang_tag_space_is_err() {
assert!(DataFactory::validate_lang_tag("en US").is_err());
}
#[test]
fn test_validate_lang_tag_double_dash_is_err() {
assert!(DataFactory::validate_lang_tag("en--US").is_err());
}
#[test]
fn test_validate_lang_tag_numeric_primary_is_err() {
assert!(DataFactory::validate_lang_tag("123").is_err());
}
#[test]
fn test_xsd_string_iri() {
assert_eq!(
xsd_types::string().as_str(),
"http://www.w3.org/2001/XMLSchema#string"
);
}
#[test]
fn test_xsd_integer_iri() {
assert_eq!(
xsd_types::integer().as_str(),
"http://www.w3.org/2001/XMLSchema#integer"
);
}
#[test]
fn test_xsd_float_iri() {
assert!(xsd_types::float().as_str().ends_with("float"));
}
#[test]
fn test_xsd_double_iri() {
assert!(xsd_types::double().as_str().ends_with("double"));
}
#[test]
fn test_xsd_boolean_iri() {
assert!(xsd_types::boolean().as_str().ends_with("boolean"));
}
#[test]
fn test_xsd_date_time_iri() {
assert!(xsd_types::date_time().as_str().ends_with("dateTime"));
}
#[test]
fn test_xsd_date_iri() {
assert!(xsd_types::date().as_str().ends_with("date"));
}
#[test]
fn test_xsd_decimal_iri() {
assert!(xsd_types::decimal().as_str().ends_with("decimal"));
}
#[test]
fn test_xsd_long_iri() {
assert!(xsd_types::long().as_str().ends_with("long"));
}
#[test]
fn test_xsd_int_iri() {
assert!(xsd_types::int().as_str().ends_with("#int"));
}
#[test]
fn test_xsd_any_uri_iri() {
assert!(xsd_types::any_uri().as_str().ends_with("anyURI"));
}
#[test]
fn test_xsd_base64_binary_iri() {
assert!(xsd_types::base64_binary()
.as_str()
.ends_with("base64Binary"));
}
#[test]
fn test_xsd_hex_binary_iri() {
assert!(xsd_types::hex_binary().as_str().ends_with("hexBinary"));
}
#[test]
fn test_vocab_rdf_type() {
assert_eq!(
vocab::rdf::r#type().as_str(),
"http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
);
}
#[test]
fn test_vocab_rdf_first_last() {
assert!(vocab::rdf::first().as_str().ends_with("first"));
assert!(vocab::rdf::rest().as_str().ends_with("rest"));
assert!(vocab::rdf::nil().as_str().ends_with("nil"));
}
#[test]
fn test_vocab_rdfs_label() {
assert_eq!(
vocab::rdfs::label().as_str(),
"http://www.w3.org/2000/01/rdf-schema#label"
);
}
#[test]
fn test_vocab_rdfs_comment() {
assert!(vocab::rdfs::comment().as_str().ends_with("comment"));
}
#[test]
fn test_vocab_rdfs_sub_class_of() {
assert!(vocab::rdfs::sub_class_of().as_str().ends_with("subClassOf"));
}
#[test]
fn test_vocab_rdfs_domain_range() {
assert!(vocab::rdfs::domain().as_str().ends_with("domain"));
assert!(vocab::rdfs::range().as_str().ends_with("range"));
}
#[test]
fn test_vocab_owl_class() {
assert_eq!(
vocab::owl::class().as_str(),
"http://www.w3.org/2002/07/owl#Class"
);
}
#[test]
fn test_vocab_owl_same_as() {
assert!(vocab::owl::same_as().as_str().ends_with("sameAs"));
}
#[test]
fn test_vocab_owl_thing_nothing() {
assert!(vocab::owl::thing().as_str().ends_with("Thing"));
assert!(vocab::owl::nothing().as_str().ends_with("Nothing"));
}
#[test]
fn test_vocab_owl_object_property() {
assert!(vocab::owl::object_property()
.as_str()
.ends_with("ObjectProperty"));
}
#[test]
fn test_vocab_xsd_string() {
assert!(vocab::xsd::string().as_str().ends_with("string"));
}
#[test]
fn test_vocab_foaf_name() {
assert_eq!(
vocab::foaf::name().as_str(),
"http://xmlns.com/foaf/0.1/name"
);
}
#[test]
fn test_vocab_foaf_person() {
assert!(vocab::foaf::person().as_str().ends_with("Person"));
}
#[test]
fn test_vocab_foaf_knows() {
assert!(vocab::foaf::knows().as_str().ends_with("knows"));
}
#[test]
fn test_roundtrip_named_node_via_string() {
let iri = "http://example.org/roundtrip";
let n = DataFactory::named_node(iri).expect("valid IRI for named node");
let s = n.as_str().to_string();
let n2 = DataFactory::named_node(s).expect("valid IRI for named node");
assert_eq!(n, n2);
}
#[test]
fn test_roundtrip_typed_literal() {
let l = DataFactory::typed_literal("123", xsd_types::integer());
let val = l.value().to_string();
let dt = l.datatype().into_owned();
let l2 = DataFactory::typed_literal(val, dt);
assert_eq!(l.value(), l2.value());
}
#[test]
fn test_roundtrip_language_literal() {
let l = DataFactory::language_literal("Hola", "es").expect("valid language literal");
let val = l.value().to_string();
let lang = l.language().expect("operation should succeed").to_string();
let l2 = DataFactory::language_literal(val, lang).expect("valid language literal");
assert_eq!(l.value(), l2.value());
assert_eq!(l.language(), l2.language());
}
#[test]
fn test_roundtrip_blank_node_with_id() {
let b = DataFactory::blank_node_with_id("stable");
let id = b.as_str().to_string();
let b2 = DataFactory::blank_node_with_id(id.clone());
assert_eq!(b2.as_str(), id);
}
#[test]
fn test_quad_default_graph_roundtrip() {
let s = DataFactory::named_node("http://example.org/s").expect("valid IRI for named node");
let p = vocab::rdf::r#type();
let o = vocab::owl::class();
let g = DataFactory::default_graph();
let q = DataFactory::quad(s.into(), p, o.into(), g.clone());
assert!(matches!(q.graph_name(), GraphName::DefaultGraph));
}
#[test]
fn test_namespace_constants() {
assert!(vocab::rdf::NAMESPACE.starts_with("http://"));
assert!(vocab::rdfs::NAMESPACE.starts_with("http://"));
assert!(vocab::owl::NAMESPACE.starts_with("http://"));
assert!(vocab::xsd::NAMESPACE.starts_with("http://"));
assert!(vocab::foaf::NAMESPACE.starts_with("http://"));
}
#[test]
fn test_xsd_types_all_in_xsd_namespace() {
let checks = [
xsd_types::string(),
xsd_types::integer(),
xsd_types::float(),
xsd_types::double(),
xsd_types::boolean(),
xsd_types::date_time(),
xsd_types::date(),
xsd_types::decimal(),
xsd_types::long(),
xsd_types::int(),
xsd_types::short(),
xsd_types::byte(),
xsd_types::unsigned_long(),
xsd_types::unsigned_int(),
xsd_types::non_negative_integer(),
xsd_types::positive_integer(),
xsd_types::any_uri(),
xsd_types::base64_binary(),
xsd_types::hex_binary(),
xsd_types::g_year(),
xsd_types::duration(),
xsd_types::time(),
xsd_types::normalized_string(),
xsd_types::token(),
];
for node in &checks {
assert!(
node.as_str()
.starts_with("http://www.w3.org/2001/XMLSchema#"),
"Not in XSD namespace: {}",
node.as_str()
);
}
}
#[test]
fn test_multiple_blank_nodes_in_triple() {
let s: Subject = DataFactory::blank_node().into();
let p = vocab::rdf::r#type();
let o: Object = DataFactory::blank_node().into();
let _t = DataFactory::triple(s, p, o);
}
#[test]
fn test_literal_with_unicode_value() {
let l = DataFactory::literal("日本語テスト");
assert_eq!(l.value(), "日本語テスト");
}
#[test]
fn test_typed_literal_float() {
let l = DataFactory::typed_literal("1.5", xsd_types::float());
assert!(l.datatype().as_str().ends_with("float"));
}
#[test]
fn test_typed_literal_decimal() {
let l = DataFactory::typed_literal("9.99", xsd_types::decimal());
assert!(l.datatype().as_str().ends_with("decimal"));
}
#[test]
fn test_rdfs_all_vocabs_are_valid_iris() {
let nodes = [
vocab::rdfs::label(),
vocab::rdfs::comment(),
vocab::rdfs::sub_class_of(),
vocab::rdfs::sub_property_of(),
vocab::rdfs::domain(),
vocab::rdfs::range(),
vocab::rdfs::class(),
vocab::rdfs::resource(),
vocab::rdfs::literal(),
vocab::rdfs::datatype(),
vocab::rdfs::is_defined_by(),
vocab::rdfs::see_also(),
vocab::rdfs::member(),
vocab::rdfs::container(),
];
for n in &nodes {
assert!(
DataFactory::validate_iri(n.as_str()).is_ok(),
"Invalid IRI for vocab node: {}",
n.as_str()
);
}
}
#[test]
fn test_rdf_all_vocabs_are_valid_iris() {
let nodes = [
vocab::rdf::r#type(),
vocab::rdf::subject(),
vocab::rdf::predicate(),
vocab::rdf::object(),
vocab::rdf::property(),
vocab::rdf::statement(),
vocab::rdf::first(),
vocab::rdf::rest(),
vocab::rdf::nil(),
vocab::rdf::list(),
vocab::rdf::bag(),
vocab::rdf::seq(),
vocab::rdf::alt(),
vocab::rdf::value(),
vocab::rdf::lang_string(),
];
for n in &nodes {
assert!(
DataFactory::validate_iri(n.as_str()).is_ok(),
"Invalid IRI: {}",
n.as_str()
);
}
}
#[test]
fn test_owl_all_vocabs_are_valid_iris() {
let nodes = [
vocab::owl::class(),
vocab::owl::object_property(),
vocab::owl::datatype_property(),
vocab::owl::annotation_property(),
vocab::owl::thing(),
vocab::owl::nothing(),
vocab::owl::same_as(),
vocab::owl::equivalent_class(),
vocab::owl::equivalent_property(),
vocab::owl::inverse_of(),
vocab::owl::disjoint_with(),
vocab::owl::functional_property(),
vocab::owl::ontology(),
];
for n in &nodes {
assert!(
DataFactory::validate_iri(n.as_str()).is_ok(),
"Invalid IRI: {}",
n.as_str()
);
}
}
}