use crate::ast::ASTComponent;
use rudof_rdf::rdf_core::parser::rdf_node_parser::constructors::{
ObjectsPropertyParser, SingleBoolPropertyParser, SingleIntegerPropertyParser,
};
use rudof_rdf::rdf_core::parser::rdf_node_parser::{ParserExt, RDFNodeParse};
use rudof_rdf::rdf_core::term::Object;
use rudof_rdf::rdf_core::term::literal::ConcreteLiteral;
use rudof_rdf::rdf_core::vocabs::ShaclVocab;
use rudof_rdf::rdf_core::{FocusRDF, RDFError, SHACLPath};
use std::collections::HashSet;
use std::marker::PhantomData;
struct QualifiedValueShapeSiblings<RDF: FocusRDF> {
_marker: PhantomData<RDF>,
property_qualified_value_shape_path: SHACLPath,
}
impl<RDF: FocusRDF> RDFNodeParse<RDF> for QualifiedValueShapeSiblings<RDF> {
type Output = Vec<Object>;
fn parse_focused(&self, rdf: &mut RDF) -> Result<Self::Output, RDFError> {
match rdf.get_focus() {
None => Err(RDFError::NoFocusNodeError),
Some(focus) => {
let mut siblings = Vec::new();
let maybe_disjoint = rdf.object_for(focus, &ShaclVocab::sh_qualified_value_shapes_disjoint().into())?;
if let Some(disjoint) = maybe_disjoint {
match disjoint {
Object::Literal(ConcreteLiteral::BooleanLiteral(true)) => {
let qvs = rdf.objects_for(focus, &ShaclVocab::sh_qualified_value_shape().into())?;
if !qvs.is_empty() {
let ps = rdf.subjects_for(&ShaclVocab::sh_property().into(), focus)?;
for property_parent in ps {
let candidate_siblings = rdf.objects_for_shacl_path(
&property_parent,
&self.property_qualified_value_shape_path,
)?;
for sibling in candidate_siblings {
if !qvs.contains(&sibling) {
let sibling_node = RDF::term_as_object(&sibling)?;
siblings.push(sibling_node);
}
}
}
}
},
Object::Literal(ConcreteLiteral::BooleanLiteral(false)) => {},
_ => { },
}
}
Ok(siblings)
},
}
}
}
pub(crate) fn qualified_value_shape<RDF: FocusRDF>() -> impl RDFNodeParse<RDF, Output = Vec<ASTComponent>> {
ObjectsPropertyParser::new(ShaclVocab::sh_qualified_value_shape())
.then(|qvs| parse_qualified_value_shape::<RDF>(qvs.into_iter().collect()))
}
fn qualified_value_shape_disjoint_parser<RDF: FocusRDF>() -> impl RDFNodeParse<RDF, Output = Option<bool>> {
SingleBoolPropertyParser::new(ShaclVocab::sh_qualified_value_shapes_disjoint()).optional()
}
fn qualified_min_count_parser<RDF: FocusRDF>() -> impl RDFNodeParse<RDF, Output = Option<isize>> {
SingleIntegerPropertyParser::new(ShaclVocab::sh_qualified_min_count()).optional()
}
fn qualified_max_count_parser<RDF: FocusRDF>() -> impl RDFNodeParse<RDF, Output = Option<isize>> {
SingleIntegerPropertyParser::new(ShaclVocab::sh_qualified_max_count()).optional()
}
fn qualified_value_shape_siblings<RDF: FocusRDF>() -> QualifiedValueShapeSiblings<RDF> {
QualifiedValueShapeSiblings {
_marker: PhantomData,
property_qualified_value_shape_path: SHACLPath::sequence(vec![
SHACLPath::iri(ShaclVocab::sh_property()),
SHACLPath::iri(ShaclVocab::sh_qualified_value_shape()),
]),
}
}
fn parse_qualified_value_shape<RDF: FocusRDF>(
qvs: HashSet<Object>,
) -> impl RDFNodeParse<RDF, Output = Vec<ASTComponent>> {
qualified_value_shape_disjoint_parser()
.and(qualified_min_count_parser())
.and(qualified_max_count_parser())
.and(qualified_value_shape_siblings())
.flat_map(move |(((maybe_disjoint, maybe_mins), maybe_maxs), siblings)| {
Ok(build_qualified_shape(
qvs.clone(),
maybe_disjoint,
maybe_mins,
maybe_maxs,
siblings,
))
})
}
fn build_qualified_shape(
terms: HashSet<Object>,
disjoint: Option<bool>,
q_min_count: Option<isize>,
q_max_count: Option<isize>,
siblings: Vec<Object>,
) -> Vec<ASTComponent> {
let mut result = Vec::new();
for term in terms {
let shape = ASTComponent::QualifiedValueShape {
shape: term,
q_min_count,
q_max_count,
disjoint,
siblings: siblings.clone(),
};
result.push(shape);
}
result
}