shacl_validation/shacl_processor.rs
1use clap::ValueEnum;
2use prefixmap::PrefixMap;
3use shacl_ast::compiled::schema::SchemaIR;
4use sparql_service::RdfData;
5use srdf::RDFFormat;
6use srdf::Rdf;
7use srdf::SRDFSparql;
8use std::fmt::Debug;
9use std::path::Path;
10
11use crate::engine::native::NativeEngine;
12use crate::engine::sparql::SparqlEngine;
13use crate::engine::Engine;
14use crate::shape::Validate;
15use crate::store::graph::Graph;
16use crate::store::sparql::Endpoint;
17use crate::store::Store;
18use crate::validate_error::ValidateError;
19use crate::validation_report::report::ValidationReport;
20
21#[derive(ValueEnum, Copy, Clone, Debug, PartialEq, Default)]
22/// Backend used for the validation.
23///
24/// According to the SHACL Recommendation, there exists no concrete method for
25/// implementing SHACL. Thus, by choosing your preferred SHACL Validation Mode,
26/// the user can select which engine is used for the validation.
27pub enum ShaclValidationMode {
28 /// We use a Rust native engine in an imperative manner (performance)
29 #[default]
30 Native,
31 /// We use a SPARQL-based engine, which is declarative
32 Sparql,
33}
34
35/// The basic operations of the SHACL Processor.
36///
37/// The ShaclProcessor trait is the one in charge of applying the SHACL
38/// Validation algorithm. For this, first, the validation report is initiliazed
39/// to empty, and, for each shape in the schema, the target nodes are
40/// selected, and then, each validator for each constraint is applied.
41pub trait ShaclProcessor<S: Rdf + Debug> {
42 fn store(&self) -> &S;
43 fn runner(&self) -> &dyn Engine<S>;
44
45 /// Executes the Validation of the provided Graph, in any of the supported
46 /// formats, against the shapes graph passed as an argument. As a result,
47 /// the Validation Report generated from the validation process is returned.
48 ///
49 /// # Arguments
50 ///
51 /// * `shapes_graph` - A compiled SHACL shapes graph
52 fn validate(&self, shapes_graph: &SchemaIR<S>) -> Result<ValidationReport, ValidateError> {
53 // we initialize the validation report to empty
54 let mut validation_results = Vec::new();
55
56 // for each shape in the schema that has at least one target
57 for (_, shape) in shapes_graph.iter_with_targets() {
58 println!("ShaclProcessor.validate with shape {}", shape.id());
59 let results = shape.validate(self.store(), self.runner(), None, Some(shape))?;
60 validation_results.extend(results);
61 }
62
63 // return the possibly empty validation report
64 Ok(ValidationReport::new()
65 .with_results(validation_results)
66 .with_prefixmap(shapes_graph.prefix_map()))
67 }
68}
69
70pub struct RdfDataValidation {
71 data: RdfData,
72 mode: ShaclValidationMode,
73}
74
75impl RdfDataValidation {
76 pub fn from_rdf_data(data: RdfData, mode: ShaclValidationMode) -> Self {
77 Self { data, mode }
78 }
79}
80
81impl ShaclProcessor<RdfData> for RdfDataValidation {
82 fn store(&self) -> &RdfData {
83 &self.data
84 }
85
86 fn runner(&self) -> &dyn Engine<RdfData> {
87 match self.mode {
88 ShaclValidationMode::Native => &NativeEngine,
89 ShaclValidationMode::Sparql => &SparqlEngine,
90 }
91 }
92}
93
94/// The In-Memory Graph Validation algorithm.
95///
96/// ```
97/// use std::path::Path;
98///
99/// use shacl_validation::shacl_processor::GraphValidation;
100/// use shacl_validation::shacl_processor::ShaclValidationMode;
101/// use shacl_validation::shacl_processor::ShaclProcessor;
102/// use shacl_validation::store::ShaclDataManager;
103/// use srdf::RDFFormat;
104///
105/// let graph_validation = GraphValidation::new(
106/// Path::new("../examples/book_conformant.ttl"), // example graph (refer to the examples folder)
107/// RDFFormat::Turtle, // serialization format of the graph
108/// None, // no base is defined
109/// ShaclValidationMode::Native, // use the Native mode (performance)
110/// )
111/// .unwrap();
112///
113/// // the following schema should generate no errors when the conforming graph
114/// // loaded in the previous declaration is used for validation
115/// let schema = std::fs::read_to_string(Path::new("../examples/book.ttl")).unwrap();
116/// let cursor = std::io::Cursor::new(schema);
117/// let compiled_schema = ShaclDataManager::load(cursor, RDFFormat::Turtle, None).unwrap();
118///
119/// let report = graph_validation.validate(&compiled_schema).unwrap();
120///
121/// assert_eq!(report.results().len(), 0);
122/// ```
123pub struct GraphValidation {
124 store: Graph,
125 mode: ShaclValidationMode,
126}
127
128impl GraphValidation {
129 /// Returns an In-Memory Graph validation SHACL processor.
130 ///
131 /// # Arguments
132 ///
133 /// * `data` - A path to the graph's serialization file
134 /// * `data_format` - Any of the possible RDF serialization formats
135 /// * `base` - An optional String, the base URI
136 /// * `mode` - Any of the possible SHACL validation modes
137 ///
138 /// # Examples
139 ///
140 /// ```
141 /// use std::path::Path;
142 ///
143 /// use shacl_validation::shacl_processor::GraphValidation;
144 /// use shacl_validation::shacl_processor::ShaclValidationMode;
145 /// use shacl_validation::shacl_processor::ShaclProcessor;
146 /// use srdf::RDFFormat;
147 ///
148 /// let graph_validation = GraphValidation::new(
149 /// Path::new("../examples/book_conformant.ttl"), // example graph (refer to the examples folder)
150 /// RDFFormat::Turtle, // serialization format of the graph
151 /// None, // no base is defined
152 /// ShaclValidationMode::Native, // use the Native mode (performance)
153 /// );
154 /// ```
155 pub fn from_path(
156 data: &Path,
157 data_format: RDFFormat,
158 base: Option<&str>,
159 mode: ShaclValidationMode,
160 ) -> Result<Self, ValidateError> {
161 Ok(GraphValidation {
162 store: Graph::from_path(data, data_format, base)?,
163 mode,
164 })
165 }
166
167 pub fn from_graph(graph: Graph, mode: ShaclValidationMode) -> GraphValidation {
168 GraphValidation { store: graph, mode }
169 }
170}
171
172impl ShaclProcessor<RdfData> for GraphValidation {
173 fn store(&self) -> &RdfData {
174 self.store.store()
175 }
176
177 fn runner(&self) -> &dyn Engine<RdfData> {
178 match self.mode {
179 ShaclValidationMode::Native => &NativeEngine,
180 ShaclValidationMode::Sparql => &SparqlEngine,
181 }
182 }
183}
184
185/// The Endpoint Graph Validation algorithm.
186pub struct EndpointValidation {
187 store: Endpoint,
188 mode: ShaclValidationMode,
189}
190
191impl EndpointValidation {
192 pub fn new(
193 iri: &str,
194 prefixmap: &PrefixMap,
195 mode: ShaclValidationMode,
196 ) -> Result<Self, ValidateError> {
197 Ok(EndpointValidation {
198 store: Endpoint::new(iri, prefixmap)?,
199 mode,
200 })
201 }
202
203 pub fn from_sparql(
204 sparql: SRDFSparql,
205 mode: ShaclValidationMode,
206 ) -> Result<Self, ValidateError> {
207 Ok(EndpointValidation {
208 store: Endpoint::from_sparql(sparql),
209 mode,
210 })
211 }
212}
213
214impl ShaclProcessor<SRDFSparql> for EndpointValidation {
215 fn store(&self) -> &SRDFSparql {
216 self.store.store()
217 }
218
219 fn runner(&self) -> &dyn Engine<SRDFSparql> {
220 match self.mode {
221 ShaclValidationMode::Native => &NativeEngine,
222 ShaclValidationMode::Sparql => &SparqlEngine,
223 }
224 }
225}