shacl_validation/
shape.rs

1use crate::engine::Engine;
2use crate::focus_nodes::FocusNodes;
3use crate::validate_error::ValidateError;
4use crate::validation_report::result::ValidationResult;
5use crate::value_nodes::ValueNodes;
6use shacl_ast::compiled::node_shape::CompiledNodeShape;
7use shacl_ast::compiled::property_shape::CompiledPropertyShape;
8use shacl_ast::compiled::shape::CompiledShape;
9use srdf::Rdf;
10use std::fmt::Debug;
11
12/// Validate RDF data using SHACL
13pub trait Validate<S: Rdf> {
14    fn validate(
15        &self,
16        store: &S,
17        runner: &dyn Engine<S>,
18        targets: Option<&FocusNodes<S>>,
19        source_shape: Option<&CompiledShape<S>>,
20    ) -> Result<Vec<ValidationResult>, ValidateError>;
21}
22
23impl<S: Rdf + Debug> Validate<S> for CompiledShape<S> {
24    fn validate(
25        &self,
26        store: &S,
27        runner: &dyn Engine<S>,
28        targets: Option<&FocusNodes<S>>,
29        source_shape: Option<&CompiledShape<S>>,
30    ) -> Result<Vec<ValidationResult>, ValidateError> {
31        println!(
32            "Shape.validate with shape {} and source shape: {}",
33            self.id(),
34            source_shape
35                .map(|s| format!("{}", s.id()))
36                .unwrap_or_else(|| "None".to_string())
37        );
38        // 0. skipping if it is deactivated
39        if *self.is_deactivated() {
40            return Ok(Vec::default());
41        }
42
43        // 1.
44        let focus_nodes = match targets {
45            Some(targets) => targets.to_owned(),
46            None => self.focus_nodes(store, runner),
47        };
48
49        // 2. Second we compute the ValueNodes; that is, the set of nodes that
50        //    are going to be used during the validation stages. This set of
51        //    nodes is obtained from the set of focus nodes
52        let value_nodes = self.value_nodes(store, &focus_nodes, runner);
53
54        // 3.
55        let component_validation_results = self.components().iter().flat_map(move |component| {
56            runner.evaluate(store, self, component, &value_nodes, source_shape)
57        });
58
59        // 4. After validating the constraints that are defined in the current
60        //    Shape, it is important to also perform the validation over those
61        //    nested PropertyShapes. The thing is that the validation needs to
62        //    occur over the focus_nodes that have been computed for the current
63        //    shape
64        let property_shapes_validation_results =
65            self.property_shapes().iter().flat_map(|prop_shape| {
66                prop_shape.validate(store, runner, Some(&focus_nodes), Some(self))
67            });
68
69        // 5.
70        let validation_results = component_validation_results
71            .chain(property_shapes_validation_results)
72            .flatten()
73            .collect();
74
75        Ok(validation_results)
76    }
77}
78
79pub trait FocusNodesOps<S: Rdf> {
80    fn focus_nodes(&self, store: &S, runner: &dyn Engine<S>) -> FocusNodes<S>;
81}
82
83impl<S: Rdf> FocusNodesOps<S> for CompiledShape<S> {
84    fn focus_nodes(&self, store: &S, runner: &dyn Engine<S>) -> FocusNodes<S> {
85        runner
86            .focus_nodes(store, self.targets())
87            .expect("Failed to retrieve focus nodes")
88    }
89}
90
91pub trait ValueNodesOps<S: Rdf> {
92    fn value_nodes(
93        &self,
94        store: &S,
95        focus_nodes: &FocusNodes<S>,
96        runner: &dyn Engine<S>,
97    ) -> ValueNodes<S>;
98}
99
100impl<S: Rdf> ValueNodesOps<S> for CompiledShape<S> {
101    fn value_nodes(
102        &self,
103        store: &S,
104        focus_nodes: &FocusNodes<S>,
105        runner: &dyn Engine<S>,
106    ) -> ValueNodes<S> {
107        match self {
108            CompiledShape::NodeShape(ns) => ns.value_nodes(store, focus_nodes, runner),
109            CompiledShape::PropertyShape(ps) => ps.value_nodes(store, focus_nodes, runner),
110        }
111    }
112}
113
114impl<S: Rdf> ValueNodesOps<S> for CompiledNodeShape<S> {
115    fn value_nodes(&self, _: &S, focus_nodes: &FocusNodes<S>, _: &dyn Engine<S>) -> ValueNodes<S> {
116        let value_nodes = focus_nodes.iter().map(|focus_node| {
117            (
118                focus_node.clone(),
119                FocusNodes::new(std::iter::once(focus_node.clone())),
120            )
121        });
122        ValueNodes::new(value_nodes)
123    }
124}
125
126impl<S: Rdf> ValueNodesOps<S> for CompiledPropertyShape<S> {
127    fn value_nodes(
128        &self,
129        store: &S,
130        focus_nodes: &FocusNodes<S>,
131        runner: &dyn Engine<S>,
132    ) -> ValueNodes<S> {
133        let value_nodes = focus_nodes.iter().filter_map(|focus_node| {
134            runner
135                .path(store, self, focus_node)
136                .ok()
137                .map(|targets| (focus_node.clone(), targets))
138        });
139        ValueNodes::new(value_nodes)
140    }
141}