use crate::{node_kind::NodeKind, value::Value};
use iri_s::{IriS, iri};
use itertools::Itertools;
use prefixmap::IriRef;
use rudof_rdf::rdf_core::vocabs::ShaclVocab;
use rudof_rdf::rdf_core::{
BuildRDF,
term::{
Object,
literal::{ConcreteLiteral, Lang},
},
};
use std::collections::HashSet;
use std::fmt::Display;
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Component {
Class(Object),
Datatype(IriRef),
NodeKind(NodeKind),
MinCount(isize),
MaxCount(isize),
MinExclusive(ConcreteLiteral),
MaxExclusive(ConcreteLiteral),
MinInclusive(ConcreteLiteral),
MaxInclusive(ConcreteLiteral),
MinLength(isize),
MaxLength(isize),
Pattern {
pattern: String,
flags: Option<String>,
},
UniqueLang(bool),
LanguageIn(Vec<Lang>),
Equals(IriRef),
Disjoint(IriRef),
LessThan(IriRef),
LessThanOrEquals(IriRef),
Or(Vec<Object>),
And(Vec<Object>),
Not(Object),
Xone(Vec<Object>),
Closed {
is_closed: bool,
ignored_properties: HashSet<IriS>,
},
Node(Object),
HasValue(Value),
In(Vec<Value>),
QualifiedValueShape {
shape: Object,
q_min_count: Option<isize>,
q_max_count: Option<isize>,
disjoint: Option<bool>,
siblings: Vec<Object>,
},
Deactivated(bool), }
impl Component {
pub fn write<B: BuildRDF>(&self, rdf_node: &Object, rdf: &mut B) -> Result<(), B::Err> {
match self {
Self::Class(rdf_node) => {
Self::write_term(&rdf_node.clone().into(), ShaclVocab::SH_CLASS, rdf_node, rdf)?;
},
Self::Datatype(iri) => {
Self::write_iri(iri, ShaclVocab::SH_DATATYPE, rdf_node, rdf)?;
},
Self::NodeKind(node_kind) => {
let iri = match &node_kind {
NodeKind::Iri => ShaclVocab::SH_IRI,
_ => unimplemented!(),
};
Self::write_iri(&IriRef::Iri(iri!(iri)), ShaclVocab::SH_DATATYPE, rdf_node, rdf)?;
},
Self::MinCount(value) => {
Self::write_integer(*value, ShaclVocab::SH_MIN_COUNT, rdf_node, rdf)?;
},
Self::MaxCount(value) => {
Self::write_integer(*value, ShaclVocab::SH_MAX_COUNT, rdf_node, rdf)?;
},
Self::MinExclusive(value) => {
Self::write_literal(value, ShaclVocab::SH_MIN_EXCLUSIVE, rdf_node, rdf)?;
},
Self::MaxExclusive(value) => {
Self::write_literal(value, ShaclVocab::SH_MAX_EXCLUSIVE, rdf_node, rdf)?;
},
Self::MinInclusive(value) => {
Self::write_literal(value, ShaclVocab::SH_MIN_INCLUSIVE, rdf_node, rdf)?;
},
Self::MaxInclusive(value) => {
Self::write_literal(value, ShaclVocab::SH_MAX_INCLUSIVE, rdf_node, rdf)?;
},
Self::MinLength(value) => {
Self::write_integer(*value, ShaclVocab::SH_MIN_LENGTH, rdf_node, rdf)?;
},
Self::MaxLength(value) => {
Self::write_integer(*value, ShaclVocab::SH_MAX_LENGTH, rdf_node, rdf)?;
},
Self::Pattern { pattern, flags } => {
Self::write_literal(&ConcreteLiteral::str(pattern), ShaclVocab::SH_PATTERN, rdf_node, rdf)?;
if let Some(flags) = flags {
Self::write_literal(&ConcreteLiteral::str(flags), ShaclVocab::SH_FLAGS, rdf_node, rdf)?;
}
},
Self::UniqueLang(value) => {
Self::write_boolean(*value, ShaclVocab::SH_UNIQUE_LANG, rdf_node, rdf)?;
},
Self::LanguageIn(langs) => {
langs.iter().try_for_each(|lang| {
Self::write_literal(
&ConcreteLiteral::str(&lang.to_string()),
ShaclVocab::SH_LANGUAGE_IN,
rdf_node,
rdf,
)
})?;
},
Self::Equals(iri) => {
Self::write_iri(iri, ShaclVocab::SH_EQUALS, rdf_node, rdf)?;
},
Self::Disjoint(iri) => {
Self::write_iri(iri, ShaclVocab::SH_DISJOINT, rdf_node, rdf)?;
},
Self::LessThan(iri) => {
Self::write_iri(iri, ShaclVocab::SH_LESS_THAN, rdf_node, rdf)?;
},
Self::LessThanOrEquals(iri) => {
Self::write_iri(iri, ShaclVocab::SH_LESS_THAN_OR_EQUALS, rdf_node, rdf)?;
},
Self::Or(shapes) => {
shapes
.iter()
.try_for_each(|shape| Self::write_term(&shape.clone().into(), ShaclVocab::SH_OR, rdf_node, rdf))?;
},
Self::And(shapes) => {
shapes
.iter()
.try_for_each(|shape| Self::write_term(&shape.clone().into(), ShaclVocab::SH_AND, rdf_node, rdf))?;
},
Self::Not(shape) => {
Self::write_term(&shape.clone().into(), ShaclVocab::SH_PATTERN, rdf_node, rdf)?;
},
Self::Xone(shapes) => {
shapes.iter().try_for_each(|shape| {
Self::write_term(&shape.clone().into(), ShaclVocab::SH_XONE, rdf_node, rdf)
})?;
},
Self::Closed {
is_closed,
ignored_properties,
} => {
Self::write_boolean(*is_closed, ShaclVocab::SH_CLOSED, rdf_node, rdf)?;
ignored_properties.iter().try_for_each(|iri| {
let iri_ref = IriRef::Iri(iri.clone());
Self::write_iri(&iri_ref, ShaclVocab::SH_IGNORED_PROPERTIES, rdf_node, rdf)
})?;
},
Self::Node(shape) => {
Self::write_term(&shape.clone().into(), ShaclVocab::SH_NODE, rdf_node, rdf)?;
},
Self::HasValue(value) => match value {
Value::Iri(iri) => {
Self::write_iri(iri, ShaclVocab::SH_HAS_VALUE, rdf_node, rdf)?;
},
Value::Literal(literal) => {
Self::write_literal(
&ConcreteLiteral::str(&literal.to_string()),
ShaclVocab::SH_HAS_VALUE,
rdf_node,
rdf,
)?;
},
},
Self::In(values) => {
values.iter().try_for_each(|value| match value {
Value::Iri(iri) => Self::write_iri(iri, ShaclVocab::SH_IN, rdf_node, rdf),
Value::Literal(literal) => Self::write_literal(
&ConcreteLiteral::str(&literal.to_string()),
ShaclVocab::SH_IN,
rdf_node,
rdf,
),
})?;
},
Self::Deactivated(value) => {
Self::write_boolean(*value, ShaclVocab::SH_DEACTIVATED, rdf_node, rdf)?;
},
Self::QualifiedValueShape {
shape,
q_min_count,
q_max_count,
disjoint,
..
} => {
Self::write_term(
&shape.clone().into(),
ShaclVocab::SH_QUALIFIED_VALUE_SHAPE,
rdf_node,
rdf,
)?;
if let Some(value) = q_min_count {
Self::write_integer(*value, ShaclVocab::SH_QUALIFIED_MIN_COUNT, rdf_node, rdf)?;
}
if let Some(value) = q_max_count {
Self::write_integer(*value, ShaclVocab::SH_QUALIFIED_MAX_COUNT, rdf_node, rdf)?;
}
if let Some(value) = disjoint {
Self::write_boolean(*value, ShaclVocab::SH_QUALIFIED_MAX_COUNT, rdf_node, rdf)?;
}
},
}
Ok(())
}
fn write_integer<B: BuildRDF>(value: isize, predicate: &str, rdf_node: &Object, rdf: &mut B) -> Result<(), B::Err> {
let value: i128 = value.try_into().unwrap();
let literal: B::Literal = value.into();
Self::write_term(&literal.into(), predicate, rdf_node, rdf)
}
fn write_boolean<B: BuildRDF>(value: bool, predicate: &str, rdf_node: &Object, rdf: &mut B) -> Result<(), B::Err> {
let literal: B::Literal = value.into();
Self::write_term(&literal.into(), predicate, rdf_node, rdf)
}
fn write_literal<B: BuildRDF>(
value: &ConcreteLiteral,
predicate: &str,
rdf_node: &Object,
rdf: &mut B,
) -> Result<(), B::Err> {
let literal: B::Literal = value.lexical_form().into();
Self::write_term(&literal.into(), predicate, rdf_node, rdf)
}
fn write_iri<B: BuildRDF>(value: &IriRef, predicate: &str, rdf_node: &Object, rdf: &mut B) -> Result<(), B::Err> {
Self::write_term(&value.get_iri().unwrap().clone().into(), predicate, rdf_node, rdf)
}
fn write_term<B: BuildRDF>(value: &B::Term, predicate: &str, rdf_node: &Object, rdf: &mut B) -> Result<(), B::Err> {
let node: B::Subject = rdf_node.clone().try_into().map_err(|_| unreachable!())?;
rdf.add_triple(node, iri!(predicate), value.clone())
}
pub fn closed(is_closed: bool, ignored_properties: HashSet<IriS>) -> Self {
Component::Closed {
is_closed,
ignored_properties,
}
}
}
impl Display for Component {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Component::Class(cls) => write!(f, "class({cls})"),
Component::Datatype(dt) => write!(f, "datatype({dt})"),
Component::NodeKind(nk) => write!(f, "nodeKind({nk})"),
Component::MinCount(mc) => write!(f, "minCount({mc})"),
Component::MaxCount(mc) => write!(f, "maxCount({mc})"),
Component::MinExclusive(me) => write!(f, "minExclusive({me})"),
Component::MaxExclusive(me) => write!(f, "maxExclusive({me})"),
Component::MinInclusive(mi) => write!(f, "minInclusive({mi})"),
Component::MaxInclusive(mi) => write!(f, "maxInclusive({mi})"),
Component::MinLength(ml) => write!(f, "minLength({ml})"),
Component::MaxLength(ml) => write!(f, "maxLength({ml})"),
Component::Pattern { pattern, flags } => match flags {
Some(flags) => write!(f, "pattern({pattern}, {flags})"),
None => write!(f, "pattern({pattern})"),
},
Component::UniqueLang(ul) => write!(f, "uniqueLang({ul})"),
Component::LanguageIn { .. } => todo!(),
Component::Equals(e) => write!(f, "equals({e})"),
Component::Disjoint(d) => write!(f, "disjoint({d})"),
Component::LessThan(lt) => write!(f, "lessThan({lt})"),
Component::LessThanOrEquals(lte) => write!(f, "lessThanOrEquals({lte})"),
Component::Or(shapes) => {
let str = shapes.iter().map(|s| s.to_string()).join(" ");
write!(f, "or [{str}]")
},
Component::And(shapes) => {
let str = shapes.iter().map(|s| s.to_string()).join(" ");
write!(f, "and [{str}]")
},
Component::Not(shape) => {
write!(f, "not [{shape}]")
},
Component::Xone(shapes) => {
let str = shapes.iter().map(|s| s.to_string()).join(" ");
write!(f, "xone [{str}]")
},
Component::Closed {
is_closed,
ignored_properties,
} => {
write!(
f,
"closed({is_closed}{})",
if ignored_properties.is_empty() {
"".to_string()
} else {
format!(
", Ignored props: [{}]",
ignored_properties.iter().map(|p| p.to_string()).join(", ")
)
}
)
},
Component::Node(shape) => write!(f, "node({shape})"),
Component::HasValue(value) => write!(f, "hasValue({value})"),
Component::In(values) => {
let str = values.iter().map(|v| v.to_string()).join(" ");
write!(f, "In [{str}]")
},
Component::QualifiedValueShape {
shape,
q_max_count,
q_min_count,
disjoint,
siblings,
} => write!(
f,
"QualifiedValueShape(shape: {shape}, qualified_min_count: {q_min_count:?}, qualified_max_count: {q_max_count:?}, qualified_value_shapes_disjoint: {disjoint:?}{})",
if siblings.is_empty() {
"".to_string()
} else {
format!(", siblings: [{}]", siblings.iter().map(|s| s.to_string()).join(", "))
}
),
Component::Deactivated(b) => write!(f, "deactivated({b})"),
}
}
}
impl From<Component> for IriS {
fn from(value: Component) -> Self {
match value {
Component::Class(_) => ShaclVocab::sh_class().clone(),
Component::Datatype(_) => ShaclVocab::sh_datatype().clone(),
Component::NodeKind(_) => ShaclVocab::sh_iri().clone(),
Component::MinCount(_) => ShaclVocab::sh_min_count().clone(),
Component::MaxCount(_) => ShaclVocab::sh_max_count().clone(),
Component::MinExclusive(_) => ShaclVocab::sh_min_exclusive().clone(),
Component::MaxExclusive(_) => ShaclVocab::sh_max_exclusive().clone(),
Component::MinInclusive(_) => ShaclVocab::sh_min_inclusive().clone(),
Component::MaxInclusive(_) => ShaclVocab::sh_max_inclusive().clone(),
Component::MinLength(_) => ShaclVocab::sh_min_length().clone(),
Component::MaxLength(_) => ShaclVocab::sh_max_length().clone(),
Component::Pattern { .. } => ShaclVocab::sh_pattern().clone(),
Component::UniqueLang(_) => ShaclVocab::sh_unique_lang().clone(),
Component::LanguageIn { .. } => ShaclVocab::sh_language_in().clone(),
Component::Equals(_) => ShaclVocab::sh_equals().clone(),
Component::Disjoint(_) => ShaclVocab::sh_disjoint().clone(),
Component::LessThan(_) => ShaclVocab::sh_less_than().clone(),
Component::LessThanOrEquals(_) => ShaclVocab::sh_less_than_or_equals().clone(),
Component::Or { .. } => ShaclVocab::sh_or().clone(),
Component::And { .. } => ShaclVocab::sh_and().clone(),
Component::Not { .. } => ShaclVocab::sh_not().clone(),
Component::Xone { .. } => ShaclVocab::sh_xone().clone(),
Component::Closed { .. } => ShaclVocab::sh_closed().clone(),
Component::Node { .. } => ShaclVocab::sh_node().clone(),
Component::HasValue { .. } => ShaclVocab::sh_has_value().clone(),
Component::In { .. } => ShaclVocab::sh_in().clone(),
Component::QualifiedValueShape { .. } => ShaclVocab::sh_qualified_value_shape().clone(),
Component::Deactivated(_) => ShaclVocab::sh_deactivated().clone(),
}
}
}