shacl_ast/compiled/
schema.rs

1use std::collections::HashMap;
2
3use prefixmap::PrefixMap;
4use srdf::Rdf;
5
6use crate::Schema;
7
8use super::compiled_shacl_error::CompiledShaclError;
9use super::shape::CompiledShape;
10
11#[derive(Debug)]
12pub struct SchemaIR<S: Rdf> {
13    // imports: Vec<IriS>,
14    // entailments: Vec<IriS>,
15    shapes: HashMap<S::Term, CompiledShape<S>>,
16    prefixmap: PrefixMap,
17    base: Option<S::IRI>,
18}
19
20impl<S: Rdf> SchemaIR<S> {
21    pub fn new(
22        shapes: HashMap<S::Term, CompiledShape<S>>,
23        prefixmap: PrefixMap,
24        base: Option<S::IRI>,
25    ) -> SchemaIR<S> {
26        SchemaIR {
27            shapes,
28            prefixmap,
29            base,
30        }
31    }
32
33    pub fn prefix_map(&self) -> PrefixMap {
34        self.prefixmap.clone()
35    }
36
37    pub fn base(&self) -> &Option<S::IRI> {
38        &self.base
39    }
40
41    pub fn iter(&self) -> impl Iterator<Item = (&S::Term, &CompiledShape<S>)> {
42        self.shapes.iter()
43    }
44
45    /// Iterate over all shapes that have at least one target.
46    pub fn iter_with_targets(&self) -> impl Iterator<Item = (&S::Term, &CompiledShape<S>)> {
47        self.shapes
48            .iter()
49            .filter(|(_, shape)| !shape.targets().is_empty())
50    }
51
52    pub fn get_shape(&self, sref: &S::Term) -> Option<&CompiledShape<S>> {
53        self.shapes.get(sref)
54    }
55}
56
57impl<S: Rdf> TryFrom<Schema> for SchemaIR<S> {
58    type Error = CompiledShaclError;
59
60    fn try_from(schema: Schema) -> Result<Self, Self::Error> {
61        let mut shapes = HashMap::default();
62
63        for (rdf_node, shape) in schema.iter() {
64            let term = rdf_node.clone().into();
65            let shape = CompiledShape::compile(shape.to_owned(), &schema)?;
66            shapes.insert(term, shape);
67        }
68
69        let prefixmap = schema.prefix_map();
70
71        let base = schema.base().map(Into::into);
72
73        Ok(SchemaIR::new(shapes, prefixmap, base))
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use std::io::Cursor;
80
81    use srdf::RDFFormat;
82    use srdf::ReaderMode;
83    use srdf::SRDFGraph;
84
85    use crate::ShaclParser;
86
87    use super::SchemaIR;
88
89    const SCHEMA: &str = r#"
90        @prefix sh: <http://www.w3.org/ns/shacl#> .
91        @prefix ex: <http://example.org/> .
92        @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
93
94        ex:PersonShape a sh:NodeShape ;
95            sh:targetClass ex:Person ;
96            sh:property [
97                sh:path ex:name ;
98                sh:datatype xsd:string ;
99                sh:minCount 1 ;
100                sh:maxCount 1 ;
101            ] ;
102            sh:property [
103                sh:path ex:age ;
104                sh:datatype xsd:integer ;
105                sh:minCount 1 ;
106                sh:maxCount 1 ;
107            ] .
108
109        ex:PersonShape2 a sh:NodeShape ;
110            sh:targetClass ex:Person ;
111            sh:property [
112                sh:path ex:name ;
113                sh:datatype xsd:string ;
114                sh:minCount 1 ;
115                sh:maxCount 1 ;
116            ] ;
117            sh:property [
118                sh:path ex:age ;
119                sh:datatype xsd:integer ;
120                sh:minCount 1 ;
121                sh:maxCount 1 ;
122            ] .
123    "#;
124
125    fn load_schema(shacl_schema: &str) -> SchemaIR<SRDFGraph> {
126        let reader = Cursor::new(shacl_schema);
127        let rdf_format = RDFFormat::Turtle;
128        let base = None;
129
130        let rdf =
131            SRDFGraph::from_reader(reader, &rdf_format, base, &ReaderMode::default()).unwrap();
132
133        ShaclParser::new(rdf).parse().unwrap().try_into().unwrap()
134    }
135
136    #[test]
137    fn test_schema_iterator() {
138        let schema = load_schema(SCHEMA);
139        let actual = schema.iter_with_targets().count();
140        let expected = 2;
141        assert_eq!(actual, expected);
142    }
143}