shapes_converter/shex_to_sparql/
shex2sparql.rs

1use prefixmap::IriRef;
2use shex_ast::{Schema, Shape, ShapeExpr, TripleExpr};
3
4use crate::shex_to_sparql::{
5    SelectQuery, ShEx2SparqlConfig, ShEx2SparqlError, TriplePattern, Var, VarBuilder,
6};
7
8pub struct ShEx2Sparql {
9    config: ShEx2SparqlConfig,
10}
11
12impl ShEx2Sparql {
13    pub fn new(config: &ShEx2SparqlConfig) -> ShEx2Sparql {
14        ShEx2Sparql {
15            config: config.clone(),
16        }
17    }
18
19    pub fn convert(
20        &self,
21        shex: &Schema,
22        maybe_shape: Option<IriRef>,
23    ) -> Result<SelectQuery, ShEx2SparqlError> {
24        let mut var_builder = VarBuilder::new();
25        match maybe_shape {
26            Some(shape) => {
27                if let Some(shape_expr) = shex.find_shape_by_iri_ref(&shape)? {
28                    shape_expr2query(&shape_expr, &self.config, shex, &mut var_builder)
29                } else {
30                    Err(ShEx2SparqlError::ShapeNotFound {
31                        iri: shape,
32                        schema: shex.clone(),
33                    })
34                }
35            }
36            None => {
37                if let Some(shape_expr) = shex.start() {
38                    shape_expr2query(&shape_expr, &self.config, shex, &mut var_builder)
39                } else {
40                    // Convert the first shape
41                    if let Some(shapes) = shex.shapes() {
42                        if let Some(shape_decl) = shapes.first() {
43                            shape_expr2query(
44                                &shape_decl.shape_expr,
45                                &self.config,
46                                shex,
47                                &mut var_builder,
48                            )
49                        } else {
50                            Err(ShEx2SparqlError::EmptyShapes {
51                                schema: shex.clone(),
52                            })
53                        }
54                    } else {
55                        Err(ShEx2SparqlError::NoShapes {
56                            schema: shex.clone(),
57                        })
58                    }
59                }
60            }
61        }
62    }
63}
64
65fn shape_expr2query(
66    shape_expr: &ShapeExpr,
67    config: &ShEx2SparqlConfig,
68    schema: &Schema,
69    var_builder: &mut VarBuilder,
70) -> Result<SelectQuery, ShEx2SparqlError> {
71    let patterns = shape_expr2patterns(shape_expr, config, schema, var_builder)?;
72    let query = SelectQuery::new()
73        .with_prefixmap(schema.prefixmap())
74        .with_base(schema.base())
75        .with_patterns(patterns);
76    Ok(query)
77}
78
79fn shape_expr2patterns(
80    se: &ShapeExpr,
81    config: &ShEx2SparqlConfig,
82    schema: &Schema,
83    var_builder: &mut VarBuilder,
84) -> Result<Vec<TriplePattern>, ShEx2SparqlError> {
85    let mut ps = Vec::new();
86    match se {
87        ShapeExpr::ShapeOr { shape_exprs: _ } => Err(ShEx2SparqlError::not_implemented("ShapeOr")),
88        ShapeExpr::ShapeAnd { shape_exprs: _ } => {
89            Err(ShEx2SparqlError::not_implemented("ShapeAND"))
90        }
91        ShapeExpr::ShapeNot { shape_expr: _ } => Err(ShEx2SparqlError::not_implemented("ShapeNot")),
92        ShapeExpr::NodeConstraint(nc) => {
93            let msg = format!("Node constraint: {nc:?}");
94            Err(ShEx2SparqlError::not_implemented(msg.as_str()))
95        }
96        ShapeExpr::Shape(s) => {
97            shape2patterns(s, &mut ps, config, schema, var_builder);
98            Ok(ps)
99        }
100        ShapeExpr::External => Err(ShEx2SparqlError::not_implemented("ShapeExternal")),
101        ShapeExpr::Ref(sref) => {
102            if let Some(shape_expr) = schema.find_shape_by_label(sref)? {
103                shape_expr2patterns(&shape_expr, config, schema, var_builder)
104            } else {
105                Err(ShEx2SparqlError::ShapeRefNotFound {
106                    sref: sref.clone(),
107                    schema: schema.clone(),
108                })
109            }
110        }
111    }
112}
113
114fn shape2patterns(
115    s: &Shape,
116    ps: &mut Vec<TriplePattern>,
117    config: &ShEx2SparqlConfig,
118    schema: &Schema,
119    var_builder: &mut VarBuilder,
120) {
121    // Maybe add triples for extra?
122    if let Some(expr) = s.triple_expr() {
123        triple_expr2patterns(&expr, ps, config, schema, var_builder)
124    }
125}
126
127fn triple_expr2patterns(
128    te: &TripleExpr,
129    ps: &mut Vec<TriplePattern>,
130    config: &ShEx2SparqlConfig,
131    schema: &Schema,
132    var_builder: &mut VarBuilder,
133) {
134    match te {
135        TripleExpr::EachOf { expressions, .. } => {
136            for tew in expressions {
137                triple_expr2patterns(&tew.te, ps, config, schema, var_builder)
138            }
139        }
140        TripleExpr::OneOf { expressions, .. } => {
141            for tew in expressions {
142                triple_expr2patterns(&tew.te, ps, config, schema, var_builder)
143            }
144        }
145        TripleExpr::TripleConstraint {
146            inverse,
147            predicate,
148            value_expr: _,
149            min: _,
150            max: _,
151            ..
152        } => {
153            // TODO: check min and max cardinalities
154            if let Some(true) = inverse {
155                todo!()
156            } else {
157                // TODO: check if value_expr is a single valuevalueset
158                let subj = Var::new(config.this_variable_name.as_str());
159                let pred = predicate;
160                let obj = var_from_predicate(predicate, schema, var_builder);
161                let tp = TriplePattern::new(&subj, pred, &obj);
162                ps.push(tp)
163            }
164        }
165        TripleExpr::TripleExprRef(_) => todo!(),
166    }
167}
168
169fn var_from_predicate(predicate: &IriRef, schema: &Schema, var_builder: &mut VarBuilder) -> Var {
170    match predicate {
171        IriRef::Iri(iri) => match schema.prefixmap() {
172            None => Var::new_from_iri(iri, var_builder),
173            Some(prefixmap) => {
174                if let Some(local) = prefixmap.qualify_local(iri) {
175                    Var::new(local.as_str())
176                } else {
177                    todo!()
178                }
179            }
180        },
181        IriRef::Prefixed { prefix: _, local } => Var::new(local),
182    }
183}
184
185#[cfg(test)]
186mod tests {
187    use super::*;
188    use shex_compact::ShExParser;
189    use spargebra::Query;
190
191    #[test]
192    fn test_simple() {
193        let shex_str = "\
194prefix : <http://example.org/>
195prefix xsd: <http://www.w3.org/2001/XMLSchema#>
196
197:Person {
198  :name xsd:string ;
199  :knows @<Person> 
200}";
201        let schema = ShExParser::parse(shex_str, None).unwrap();
202        let query_str = "\
203prefix : <http://example.org/>
204prefix xsd: <http://www.w3.org/2001/XMLSchema#>
205
206Select * where {
207    ?this :name  ?name  .
208    ?this :knows ?knows  
209}";
210        let expected_query = Query::parse(query_str, None).unwrap();
211        let converter = ShEx2Sparql::new(&ShEx2SparqlConfig::default());
212        let converted_query = converter.convert(&schema, None).unwrap();
213        let converted_query_str = format!("{}", converted_query);
214        let converted_query_parsed = Query::parse(converted_query_str.as_str(), None).unwrap();
215        assert_eq!(converted_query_parsed, expected_query);
216    }
217}