1use crate::atom;
2use crate::validator_error::*;
3use crate::validator_runner::Engine;
4use crate::Reason;
5use crate::ValidatorConfig;
6use either::Either;
7use prefixmap::IriRef;
8use prefixmap::PrefixMap;
9use serde_json::Value;
10use shapemap::query_shape_map::QueryShapeMap;
11use shapemap::ResultShapeMap;
12use shapemap::ValidationStatus;
13use shex_ast::ir::schema_ir::SchemaIR;
14use shex_ast::ir::shape_expr::ShapeExpr;
15use shex_ast::ir::shape_label::ShapeLabel;
16use shex_ast::object_value::ObjectValue;
17use shex_ast::Node;
18use shex_ast::ShapeExprLabel;
19use shex_ast::ShapeLabelIdx;
20use srdf::Query;
21use tracing::debug;
22
23type Result<T> = std::result::Result<T, ValidatorError>;
24type Atom = atom::Atom<(Node, ShapeLabelIdx)>;
25
26#[derive(Debug)]
27pub struct Validator {
28 schema: SchemaIR,
29 config: ValidatorConfig,
30}
31
32impl Validator {
33 pub fn new(schema: SchemaIR, config: &ValidatorConfig) -> Result<Validator> {
34 if config.check_negation_requirement.unwrap_or(true) && schema.has_neg_cycle() {
35 let neg_cycles = schema.neg_cycles();
36 let mut neg_cycles_displayed = Vec::new();
37 for cycle in neg_cycles.iter() {
38 let mut cycle_displayed = Vec::new();
39 for (source, target, shapes) in cycle.iter() {
40 let source_str = if let Some(label) = schema.shape_label_from_idx(source) {
41 schema.show_label(label)
42 } else {
43 format!("internal_{source}")
44 };
45 let target_str = if let Some(label) = schema.shape_label_from_idx(target) {
46 schema.show_label(label)
47 } else {
48 format!("internal_{target}")
49 };
50 let mut shapes_str = Vec::new();
51 for shape in shapes.iter() {
52 let shape_str = if let Some(label) = schema.shape_label_from_idx(shape) {
53 schema.show_label(label)
54 } else {
55 format!("internal_{shape}")
56 };
57 shapes_str.push(shape_str);
58 }
59 cycle_displayed.push((source_str, target_str, shapes_str));
60 }
61 neg_cycles_displayed.push(cycle_displayed);
62 }
63 return Err(ValidatorError::NegCycleError {
64 neg_cycles: neg_cycles_displayed,
65 });
66 }
67 Ok(Validator {
69 schema,
70 config: config.clone(),
71 })
72 }
73
74 pub fn schema(&self) -> &SchemaIR {
79 &self.schema
80 }
81
82 pub fn validate_node_shape<S>(
84 &mut self,
85 node: &Node,
86 shape: &ShapeLabel,
87 rdf: &S,
88 schema: &SchemaIR,
89 maybe_nodes_prefixmap: &Option<PrefixMap>,
90 maybe_shapes_prefixmap: &Option<PrefixMap>,
91 ) -> Result<ResultShapeMap>
92 where
93 S: Query,
94 {
95 let idx = self.get_idx(shape)?;
96 let mut engine = Engine::new(&self.config);
97 engine.add_pending(node.clone(), idx);
98 debug!("Before while loop: ${}@{}", node, idx);
99 self.loop_validating(&mut engine, rdf, schema)?;
100 let result = self.result_map(&mut engine, maybe_nodes_prefixmap, maybe_shapes_prefixmap)?;
101 Ok(result)
102 }
103
104 fn get_shape_expr_label(
105 &self,
106 label: &ShapeExprLabel,
107 schema: &SchemaIR,
108 ) -> Result<ShapeLabelIdx> {
109 schema
110 .find_ref(label)
111 .map_err(|error| ValidatorError::ShapeLabelNotFoundError {
112 shape_label: label.clone(),
113 error: format!("{error}"),
114 })
115 }
116
117 pub fn validate_shapemap<S>(
118 &self,
119 shapemap: &QueryShapeMap,
120 rdf: &S,
121 schema: &SchemaIR,
122 maybe_nodes_prefixmap: &Option<PrefixMap>,
123 maybe_shapes_prefixmap: &Option<PrefixMap>,
124 ) -> Result<ResultShapeMap>
125 where
126 S: Query,
127 {
128 let mut engine = Engine::new(&self.config);
129 self.fill_pending(&mut engine, shapemap, rdf, schema)?;
130 self.loop_validating(&mut engine, rdf, schema)?;
131 let result = self.result_map(&mut engine, maybe_nodes_prefixmap, maybe_shapes_prefixmap)?;
132 Ok(result)
133 }
134
135 pub fn validate_shapemap2<S>(
136 &self,
137 shapemap: &QueryShapeMap,
138 rdf: &S,
139 schema: &SchemaIR,
140 maybe_nodes_prefixmap: &Option<PrefixMap>,
141 maybe_shapes_prefixmap: &Option<PrefixMap>,
142 ) -> Result<ResultShapeMap>
143 where
144 S: Query,
145 {
146 let mut engine = Engine::new(&self.config);
147 self.fill_pending(&mut engine, shapemap, rdf, schema)?;
148 engine.validate_pending(rdf, schema)?;
149 let result = self.result_map(&mut engine, maybe_nodes_prefixmap, maybe_shapes_prefixmap)?;
150 Ok(result)
151 }
152
153 fn fill_pending<S>(
154 &self,
155 engine: &mut Engine,
156 shapemap: &QueryShapeMap,
157 rdf: &S,
158 schema: &SchemaIR,
159 ) -> Result<()>
160 where
161 S: Query,
162 {
163 for (node_value, label) in shapemap.iter_node_shape(rdf) {
164 let idx = self.get_shape_expr_label(label, schema)?;
165 let node = self.node_from_object_value(node_value, rdf)?;
166 engine.add_pending(node.clone(), idx);
167 }
168 Ok(())
169 }
170
171 fn node_from_object_value<S>(&self, value: &ObjectValue, rdf: &S) -> Result<Node>
172 where
173 S: Query,
174 {
175 match value {
176 ObjectValue::IriRef(IriRef::Iri(iri)) => Ok(Node::iri(iri.clone())),
177 ObjectValue::IriRef(IriRef::Prefixed { prefix, local }) => {
178 let iri = rdf.resolve_prefix_local(prefix, local)?;
179 Ok(Node::iri(iri.clone()))
180 }
181 ObjectValue::Literal(lit) => Ok(Node::literal(lit.clone())),
182 }
183 }
184
185 fn loop_validating<S>(&self, engine: &mut Engine, rdf: &S, schema: &SchemaIR) -> Result<()>
186 where
187 S: Query,
188 {
189 while engine.no_end_steps() && engine.more_pending() {
190 engine.new_step();
191 let atom = engine.pop_pending().unwrap();
192 debug!("Processing atom: {}", show(&atom));
193 engine.add_processing(&atom);
194 let passed = self.check_node_atom(engine, &atom, rdf, schema)?;
195 engine.remove_processing(&atom);
196 match passed {
197 Either::Right(reasons) => {
198 engine.add_checked_pos(atom, reasons);
199 }
200 Either::Left(errors) => {
201 engine.add_checked_neg(atom.negated(), errors);
202 }
203 }
204 }
205 Ok(())
206 }
207
208 pub fn check_node_atom<S>(
209 &self,
210 engine: &mut Engine,
211 atom: &Atom,
212 rdf: &S,
213 schema: &SchemaIR,
214 ) -> Result<Either<Vec<ValidatorError>, Vec<Reason>>>
215 where
216 S: Query,
217 {
218 let (node, idx) = atom.get_value();
219 let se = find_shape_idx(idx, &self.schema);
220 match atom {
221 Atom::Pos { .. } => engine.check_node_shape_expr_old(node, se, rdf, schema),
222 Atom::Neg { .. } => {
223 todo!()
225 }
226 }
227 }
228
229 fn get_idx(&self, shape: &ShapeLabel) -> Result<ShapeLabelIdx> {
230 match self.schema.find_label(shape) {
231 Some((idx, _se)) => Ok(*idx),
232 None => Err(ValidatorError::NotFoundShapeLabel {
233 shape: (*shape).clone(),
234 }),
235 }
236 }
237
238 fn get_shape_label(&self, idx: &ShapeLabelIdx) -> Result<&ShapeLabel> {
239 let (label, _se) = self.schema.find_shape_idx(idx).unwrap();
240 match label {
241 Some(label) => Ok(label),
242 None => Err(ValidatorError::NotFoundShapeLabelWithIndex { idx: *idx }),
243 }
244 }
245
246 pub fn result_map(
247 &self,
248 engine: &mut Engine,
249 maybe_nodes_prefixmap: &Option<PrefixMap>,
250 maybe_shapes_prefixmap: &Option<PrefixMap>,
251 ) -> Result<ResultShapeMap> {
252 let mut result = match (maybe_nodes_prefixmap, maybe_shapes_prefixmap) {
253 (None, None) => ResultShapeMap::new(),
254 (Some(npm), None) => ResultShapeMap::new().with_nodes_prefixmap(npm),
255 (None, Some(spm)) => ResultShapeMap::new().with_shapes_prefixmap(spm),
256 (Some(npm), Some(spm)) => ResultShapeMap::new()
257 .with_nodes_prefixmap(npm)
258 .with_shapes_prefixmap(spm),
259 };
260 for atom in &engine.checked() {
261 let (node, idx) = atom.get_value();
262 let label = self.get_shape_label(idx)?;
263 match atom {
264 Atom::Pos(pa) => {
265 let reasons = engine.find_reasons(pa);
266 let status = ValidationStatus::conformant(
267 show_reasons(&reasons),
268 json_reasons(&reasons),
269 );
270 result
272 .add_result((*node).clone(), label.clone(), status)
273 .map_err(|e| ValidatorError::AddingConformantError {
274 node: node.to_string(),
275 label: label.to_string(),
276 error: format!("{e}"),
277 })?;
278 }
279 Atom::Neg(na) => {
280 let errors = engine.find_errors(na);
281 let status = ValidationStatus::non_conformant(
282 show_errors(&errors),
283 json_errors(&errors),
284 );
285 result
286 .add_result((*node).clone(), label.clone(), status)
287 .map_err(|e| ValidatorError::AddingNonConformantError {
288 node: node.to_string(),
289 label: label.to_string(),
290 error: format!("{e}"),
291 })?;
292 }
293 }
294 }
295 for atom in &engine.pending() {
296 let (node, idx) = atom.get_value();
297 let label = self.get_shape_label(idx)?;
298 let status = ValidationStatus::pending();
299 result
300 .add_result((*node).clone(), label.clone(), status)
301 .map_err(|e| ValidatorError::AddingPendingError {
302 node: node.to_string(),
303 label: label.to_string(),
304 error: format!("{e}"),
305 })?;
306 }
307 Ok(result)
308 }
309
310 pub fn shapes_prefixmap(&self) -> PrefixMap {
311 self.schema.prefixmap()
312 }
313}
314
315fn find_shape_idx<'a>(idx: &'a ShapeLabelIdx, schema: &'a SchemaIR) -> &'a ShapeExpr {
316 let (_label, se) = schema.find_shape_idx(idx).unwrap();
317 se
318}
319
320fn show_errors(errors: &[ValidatorError]) -> String {
321 let mut result = String::new();
322 for (err, idx) in errors.iter().enumerate() {
323 result.push_str(format!("Error #{idx}: {err}\n").as_str());
324 }
325 result
326}
327
328fn json_errors(_errors: &[ValidatorError]) -> Value {
329 let vs = vec!["todo", "errors"];
330 vs.into()
331}
332
333fn json_reasons(reasons: &[Reason]) -> Value {
334 let value = Value::Array(reasons.iter().map(|reason| reason.as_json()).collect());
335 value
336}
337
338fn show_reasons(reasons: &[Reason]) -> String {
339 let mut result = String::new();
340 for (reason, idx) in reasons.iter().enumerate() {
341 result.push_str(format!("Reason #{idx}: {reason}\n").as_str());
342 }
343 result
344}
345
346fn show(atom: &Atom) -> String {
347 match atom {
348 Atom::Pos((node, idx)) => format!("+({node},{idx})"),
349 Atom::Neg((node, idx)) => format!("!({node},{idx})"),
350 }
351}
352
353#[cfg(test)]
354mod tests {}