shacl_validation/validation_report/
result.rs

1use super::validation_report_error::{ReportError, ResultError};
2use crate::helpers::srdf::get_object_for;
3use shacl_ast::*;
4use srdf::{Object, Query, RDFNode, SRDFBuilder};
5use std::fmt::Debug;
6
7#[derive(Debug, Clone, PartialEq)]
8pub struct ValidationResult {
9    focus_node: RDFNode,           // required
10    path: Option<RDFNode>,         // optional
11    value: Option<RDFNode>,        // optional
12    source: Option<RDFNode>,       // optional
13    constraint_component: RDFNode, // required
14    details: Option<Vec<RDFNode>>, // optional
15    message: Option<RDFNode>,      // optional
16    severity: RDFNode,             // required (TODO: Replace by Severity?)
17}
18
19impl ValidationResult {
20    // Creates a new validation result
21    pub fn new(focus_node: Object, constraint_component: Object, severity: Object) -> Self {
22        Self {
23            focus_node,
24            path: None,
25            value: None,
26            source: None,
27            constraint_component,
28            details: None,
29            message: None,
30            severity,
31        }
32    }
33
34    pub fn with_path(mut self, path: Option<Object>) -> Self {
35        self.path = path;
36        self
37    }
38
39    pub fn with_value(mut self, value: Option<Object>) -> Self {
40        self.value = value;
41        self
42    }
43
44    pub fn with_source(mut self, source: Option<Object>) -> Self {
45        self.source = source;
46        self
47    }
48
49    pub fn with_details(mut self, details: Option<Vec<Object>>) -> Self {
50        self.details = details;
51        self
52    }
53
54    pub fn with_message(mut self, message: Option<Object>) -> Self {
55        self.message = message;
56        self
57    }
58
59    pub fn source(&self) -> Option<&Object> {
60        self.source.as_ref()
61    }
62
63    pub fn value(&self) -> Option<&Object> {
64        self.value.as_ref()
65    }
66
67    pub fn focus_node(&self) -> &Object {
68        &self.focus_node
69    }
70
71    pub fn component(&self) -> &Object {
72        &self.constraint_component
73    }
74
75    pub fn severity(&self) -> &Object {
76        &self.severity
77    }
78}
79
80impl ValidationResult {
81    pub(crate) fn parse<S: Query>(
82        store: &S,
83        validation_result: &S::Term,
84    ) -> Result<Self, ResultError> {
85        // 1. First, we must start processing the required fields. In case some
86        //    don't appear, an error message must be raised
87        let focus_node =
88            match get_object_for(store, validation_result, &SH_FOCUS_NODE.clone().into())? {
89                Some(focus_node) => focus_node,
90                None => return Err(ResultError::MissingRequiredField("FocusNode".to_owned())),
91            };
92        let severity =
93            match get_object_for(store, validation_result, &SH_RESULT_SEVERITY.clone().into())? {
94                Some(severity) => severity,
95                None => return Err(ResultError::MissingRequiredField("Severity".to_owned())),
96            };
97        let constraint_component = match get_object_for(
98            store,
99            validation_result,
100            &SH_SOURCE_CONSTRAINT_COMPONENT.clone().into(),
101        )? {
102            Some(constraint_component) => constraint_component,
103            None => {
104                return Err(ResultError::MissingRequiredField(
105                    "SourceConstraintComponent".to_owned(),
106                ))
107            }
108        };
109
110        // 2. Second, we must process the optional fields
111        let path = get_object_for(store, validation_result, &SH_RESULT_PATH.clone().into())?;
112        let source = get_object_for(store, validation_result, &SH_SOURCE_SHAPE.clone().into())?;
113        let value = get_object_for(store, validation_result, &SH_VALUE.clone().into())?;
114
115        // 3. Lastly we build the ValidationResult
116        Ok(
117            ValidationResult::new(focus_node, constraint_component, severity)
118                .with_path(path)
119                .with_source(source)
120                .with_value(value),
121        )
122    }
123
124    pub fn to_rdf<RDF>(
125        &self,
126        rdf_writer: &mut RDF,
127        report_node: RDF::Subject,
128    ) -> Result<(), ReportError>
129    where
130        RDF: SRDFBuilder + Sized,
131    {
132        rdf_writer
133            .add_type(report_node.clone(), SH_VALIDATION_RESULT.clone())
134            .map_err(|e| ReportError::ValidationReportError { msg: e.to_string() })?;
135        rdf_writer
136            .add_triple(
137                report_node.clone(),
138                SH_FOCUS_NODE.clone(),
139                self.focus_node.clone(),
140            )
141            .map_err(|e| ReportError::ValidationReportError {
142                msg: format!("Error adding focus node to validation result: {e}"),
143            })?;
144        rdf_writer
145            .add_triple(
146                report_node.clone(),
147                SH_SOURCE_CONSTRAINT_COMPONENT.clone(),
148                self.constraint_component.clone(),
149            )
150            .map_err(|e| ReportError::ValidationReportError {
151                msg: format!("Error adding source constraint component to validation result: {e}"),
152            })?;
153        rdf_writer
154            .add_triple(
155                report_node.clone(),
156                SH_RESULT_SEVERITY.clone(),
157                self.severity.clone(),
158            )
159            .map_err(|e| ReportError::ValidationReportError {
160                msg: format!("Error adding severity to validation result: {e}"),
161            })?;
162        let message = match self.message {
163            Some(ref message) => message.clone(),
164            None => Object::str("No message"),
165        };
166        rdf_writer
167            .add_triple(report_node.clone(), SH_RESULT_MESSAGE.clone(), message)
168            .map_err(|e| ReportError::ValidationReportError {
169                msg: format!("Error result message to validation result: {e}"),
170            })?;
171        if let Some(source) = &self.source {
172            let source_term: RDF::Term = source.clone().into();
173            rdf_writer
174                .add_triple(report_node.clone(), SH_SOURCE_SHAPE.clone(), source_term)
175                .map_err(|e| ReportError::ValidationReportError {
176                    msg: format!("Error adding source to validation result: {e}"),
177                })?;
178        }
179
180        Ok(())
181    }
182}