shacl_ast/ast/
property_shape.rs

1use std::fmt::Display;
2
3use iri_s::iri;
4use srdf::{numeric_literal::NumericLiteral, RDFNode, SHACLPath, SRDFBuilder};
5
6use crate::{
7    component::Component, message_map::MessageMap, severity::Severity, target::Target,
8    SH_DEACTIVATED, SH_DESCRIPTION, SH_GROUP, SH_INFO_STR, SH_NAME, SH_ORDER, SH_PATH,
9    SH_PROPERTY_SHAPE, SH_SEVERITY, SH_VIOLATION_STR, SH_WARNING_STR,
10};
11
12#[derive(Debug, Clone)]
13pub struct PropertyShape {
14    id: RDFNode,
15    path: SHACLPath,
16    components: Vec<Component>,
17    targets: Vec<Target>,
18    property_shapes: Vec<RDFNode>,
19    closed: bool,
20    // ignored_properties: Vec<IriRef>,
21    deactivated: bool,
22    // message: MessageMap,
23    severity: Option<Severity>,
24    name: MessageMap,
25    description: MessageMap,
26    order: Option<NumericLiteral>,
27    group: Option<RDFNode>,
28    // source_iri: Option<IriRef>,
29    // annotations: Vec<(IriRef, RDFNode)>,
30}
31
32impl PropertyShape {
33    pub fn new(id: RDFNode, path: SHACLPath) -> Self {
34        PropertyShape {
35            id,
36            path,
37            components: Vec::new(),
38            targets: Vec::new(),
39            property_shapes: Vec::new(),
40            closed: false,
41            // ignored_properties: Vec::new(),
42            deactivated: false,
43            // message: MessageMap::new(),
44            severity: None,
45            name: MessageMap::new(),
46            description: MessageMap::new(),
47            order: None,
48            group: None,
49            // source_iri: None,
50            // annotations: Vec::new()
51        }
52    }
53
54    pub fn with_name(mut self, name: MessageMap) -> Self {
55        self.name = name;
56        self
57    }
58    pub fn with_description(mut self, description: MessageMap) -> Self {
59        self.description = description;
60        self
61    }
62
63    pub fn with_order(mut self, order: Option<NumericLiteral>) -> Self {
64        self.order = order;
65        self
66    }
67
68    pub fn with_group(mut self, group: Option<RDFNode>) -> Self {
69        self.group = group;
70        self
71    }
72
73    pub fn with_targets(mut self, targets: Vec<Target>) -> Self {
74        self.targets = targets;
75        self
76    }
77
78    pub fn with_property_shapes(mut self, property_shapes: Vec<RDFNode>) -> Self {
79        self.property_shapes = property_shapes;
80        self
81    }
82
83    pub fn with_components(mut self, components: Vec<Component>) -> Self {
84        self.components = components;
85        self
86    }
87
88    pub fn with_closed(mut self, closed: bool) -> Self {
89        self.closed = closed;
90        self
91    }
92
93    pub fn with_severity(mut self, severity: Option<Severity>) -> Self {
94        self.severity = severity;
95        self
96    }
97
98    pub fn id(&self) -> &RDFNode {
99        &self.id
100    }
101
102    pub fn path(&self) -> &SHACLPath {
103        &self.path
104    }
105
106    pub fn name(&self) -> &MessageMap {
107        &self.name
108    }
109
110    pub fn description(&self) -> &MessageMap {
111        &self.description
112    }
113
114    pub fn is_closed(&self) -> &bool {
115        &self.closed
116    }
117
118    pub fn is_deactivated(&self) -> &bool {
119        &self.deactivated
120    }
121
122    pub fn severity(&self) -> Option<Severity> {
123        self.severity.to_owned()
124    }
125
126    pub fn components(&self) -> &Vec<Component> {
127        &self.components
128    }
129
130    pub fn targets(&self) -> &Vec<Target> {
131        &self.targets
132    }
133
134    pub fn property_shapes(&self) -> &Vec<RDFNode> {
135        &self.property_shapes
136    }
137
138    // pub fn get_value_nodes(
139    //     &self,
140    //     data_graph: &SRDFGraph,
141    //     focus_node: &RDFNode,
142    //     path: &SHACLPath,
143    // ) -> HashSet<RDFNode> {
144    //     match path {
145    //         SHACLPath::Predicate { pred } => {
146    //             let subject = match focus_node {
147    //                 RDFNode::Iri(iri_s) => Subject::NamedNode(iri_s.as_named_node().to_owned()),
148    //                 RDFNode::BlankNode(id) => Subject::BlankNode(BlankNode::new_unchecked(id)),
149    //                 RDFNode::Literal(_) => todo!(),
150    //             };
151    //             if let Ok(objects) =
152    //                 data_graph.objects_for_subject_predicate(&subject, pred.as_named_node())
153    //             {
154    //                 objects
155    //                     .into_iter()
156    //                     .map(|object| match object {
157    //                         Term::NamedNode(node) => {
158    //                             RDFNode::iri(IriS::new_unchecked(node.as_str()))
159    //                         }
160    //                         Term::BlankNode(node) => RDFNode::bnode(node.to_string()),
161    //                         Term::Literal(literal) => RDFNode::literal(literal.into()),
162    //                         #[cfg(feature = "rdf-star")]
163    //                         Term::Triple(_) => unimplemented!(),
164    //                     })
165    //                     .collect::<HashSet<RDFNode>>()
166    //             } else {
167    //                 HashSet::new()
168    //             }
169    //         }
170    //         SHACLPath::Alternative { .. } => todo!(),
171    //         SHACLPath::Sequence { .. } => todo!(),
172    //         SHACLPath::Inverse { .. } => todo!(),
173    //         SHACLPath::ZeroOrMore { .. } => todo!(),
174    //         SHACLPath::OneOrMore { .. } => todo!(),
175    //         SHACLPath::ZeroOrOne { .. } => todo!(),
176    //     }
177    // }
178
179    // TODO: this is a bit ugly
180    pub fn write<RDF>(&self, rdf: &mut RDF) -> Result<(), RDF::Err>
181    where
182        RDF: SRDFBuilder,
183    {
184        let id: RDF::Subject = self.id.clone().try_into().map_err(|_| unreachable!())?;
185        rdf.add_type(id.clone(), SH_PROPERTY_SHAPE.clone())?;
186
187        self.name.iter().try_for_each(|(lang, value)| {
188            let literal: RDF::Literal = match lang {
189                Some(_) => todo!(),
190                None => value.clone().into(),
191            };
192            rdf.add_triple(id.clone(), SH_NAME.clone(), literal)
193        })?;
194
195        self.description.iter().try_for_each(|(lang, value)| {
196            let literal: RDF::Literal = match lang {
197                Some(_) => todo!(),
198                None => value.clone().into(),
199            };
200            rdf.add_triple(id.clone(), SH_DESCRIPTION.clone(), literal)
201        })?;
202
203        if let Some(order) = self.order.clone() {
204            let literal: RDF::Literal = match order {
205                NumericLiteral::Decimal(_) => todo!(),
206                NumericLiteral::Double(float) => float.into(),
207                NumericLiteral::Integer(int) => {
208                    let i: i128 = int.try_into().unwrap();
209                    i.into()
210                }
211            };
212            rdf.add_triple(id.clone(), SH_ORDER.clone(), literal)?;
213        }
214
215        if let Some(group) = &self.group {
216            rdf.add_triple(id.clone(), SH_GROUP.clone(), group.clone())?;
217        }
218
219        if let SHACLPath::Predicate { pred } = &self.path {
220            rdf.add_triple(id.clone(), SH_PATH.clone(), pred.clone())?;
221        } else {
222            unimplemented!()
223        }
224
225        self.components
226            .iter()
227            .try_for_each(|component| component.write(&self.id, rdf))?;
228
229        self.targets
230            .iter()
231            .try_for_each(|target| target.write(&self.id, rdf))?;
232
233        if self.deactivated {
234            let literal: RDF::Literal = "true".to_string().into();
235
236            rdf.add_triple(id.clone(), SH_DEACTIVATED.clone(), literal)?;
237        }
238
239        if let Some(severity) = &self.severity {
240            let pred = match severity {
241                Severity::Violation => iri!(SH_VIOLATION_STR),
242                Severity::Info => iri!(SH_INFO_STR),
243                Severity::Warning => iri!(SH_WARNING_STR),
244                Severity::Generic(iri) => iri.get_iri().unwrap(),
245            };
246
247            rdf.add_triple(id.clone(), SH_SEVERITY.clone(), pred.clone())?;
248        }
249
250        Ok(())
251    }
252}
253
254impl Display for PropertyShape {
255    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
256        writeln!(f, "{{")?;
257        writeln!(f, "       PropertyShape")?;
258        writeln!(f, "       path: {}", self.path)?;
259        for target in self.targets.iter() {
260            writeln!(f, "       {target}")?
261        }
262        if self.closed {
263            writeln!(f, "       closed: {}", self.closed)?
264        }
265        for property in self.property_shapes.iter() {
266            writeln!(f, "       Property {property}")?
267        }
268        for component in self.components.iter() {
269            writeln!(f, "       {component}")?
270        }
271        write!(f, "}}")?;
272        Ok(())
273    }
274}