Skip to main content

swls_core/systems/shapes/
data.rs

1use std::{borrow::Cow, cmp::Ordering, fmt::Display, sync::Arc};
2
3use bevy_ecs::prelude::*;
4use iri_s::IriS;
5use prefixmap::{error::PrefixMapError, PrefixMap};
6use rudof_rdf::rdf_core::{
7    term::{
8        self as r_term, literal::Literal, BlankNode, Iri, IriOrBlankNode, Term as Rudof_Term,
9        TermKind,
10    },
11    FocusRDF, Matcher, NeighsRDF, RDFError, SHACLPath,
12};
13use shacl_ir::compiled::schema_ir::SchemaIR as ShaclSchemaIR;
14use sophia_api::term::Term;
15
16use crate::prelude::*;
17
18#[derive(Component)]
19pub struct ShaclShapes {
20    shacl: ShaclSchemaIR,
21}
22impl ShaclShapes {
23    pub fn new(shacl: ShaclSchemaIR) -> Self {
24        Self { shacl }
25    }
26
27    pub fn ir(&self) -> &ShaclSchemaIR {
28        &self.shacl
29    }
30}
31unsafe impl Send for ShaclShapes {}
32
33#[derive(Clone, Debug)]
34pub struct Rdf {
35    focus: Option<MyTerm<'static>>,
36    triples: Arc<Vec<MyQuad<'static>>>,
37}
38impl Rdf {
39    pub fn new(triples: &Triples) -> Self {
40        Self {
41            focus: None,
42            triples: triples.0.clone(),
43        }
44    }
45}
46
47impl FocusRDF for Rdf {
48    fn set_focus(&mut self, focus: &Self::Term) {
49        self.focus = Some(focus.clone());
50    }
51
52    fn get_focus(&self) -> Option<&Self::Term> {
53        self.focus.as_ref()
54    }
55}
56
57impl NeighsRDF for Rdf {
58    fn triples(&self) -> std::result::Result<impl Iterator<Item = Self::Triple>, Self::Err> {
59        Ok(self.triples.iter().cloned())
60    }
61
62    fn triples_matching<S, P, O>(
63        &self,
64        subject: &S,
65        predicate: &P,
66        object: &O,
67    ) -> Result<impl Iterator<Item = Self::Triple> + '_, Self::Err>
68    where
69        S: Matcher<Self::Subject>,
70        P: Matcher<Self::IRI>,
71        O: Matcher<Self::Term>,
72    {
73        let subject: Option<MyTerm<'static>> = subject.value().cloned();
74        let predicate: Option<MyTerm<'static>> = predicate.value().cloned();
75        let object: Option<MyTerm<'static>> = object.value().cloned();
76        let triples = self.triples()?.filter_map(move |triple| {
77            let subject_matches = subject
78                .as_ref()
79                .map(|s| s == &triple.subject)
80                .unwrap_or(true);
81
82            let predicate_matches = predicate
83                .as_ref()
84                .map(|s| s == &triple.predicate)
85                .unwrap_or(true);
86
87            let object_matches = object.as_ref().map(|s| s == &triple.object).unwrap_or(true);
88
89            if subject_matches && predicate_matches && object_matches {
90                Some(triple)
91            } else {
92                None
93            }
94        });
95        Ok(triples)
96    }
97}
98
99impl<'b> TryInto<oxrdf::Term> for MyTerm<'static> {
100    type Error = ();
101
102    fn try_into(self) -> std::result::Result<oxrdf::Term, Self::Error> {
103        use oxigraph::{model as M, model::Term as T};
104        let output = match &self.ty {
105            Some(sophia_api::prelude::TermKind::Iri) => {
106                T::NamedNode(M::NamedNode::new(self.as_str()).map_err(|_| ())?)
107            }
108            Some(sophia_api::prelude::TermKind::Literal) => {
109                if let Some(dt) = sophia_api::prelude::Term::datatype(&self) {
110                    let dt = M::NamedNode::new(dt.as_str()).map_err(|_| ())?;
111                    T::Literal(M::Literal::new_typed_literal(self.value.as_ref(), dt))
112                } else if let Some(lang) = self.language_tag() {
113                    T::Literal(
114                        M::Literal::new_language_tagged_literal(self.value.as_ref(), lang.as_str())
115                            .map_err(|_| ())?,
116                    )
117                } else {
118                    T::Literal(M::Literal::new_simple_literal(self.value.as_ref()))
119                }
120            }
121            Some(sophia_api::prelude::TermKind::BlankNode) => {
122                T::BlankNode(M::BlankNode::new(self.value.as_ref()).map_err(|_| ())?)
123            }
124            _ => {
125                return Err(());
126            }
127        };
128        return Ok(output);
129    }
130}
131
132impl TryFrom<MyTerm<'static>> for IriOrBlankNode {
133    type Error = ();
134
135    fn try_from(term: MyTerm<'static>) -> std::result::Result<Self, Self::Error> {
136        let MyTerm { ty, value, .. } = term;
137        if let Some(t) = ty {
138            match t {
139                sophia_api::term::TermKind::Iri => {
140                    return Ok(IriOrBlankNode::Iri(IriS::new_unchecked(value.as_ref())))
141                }
142                sophia_api::term::TermKind::BlankNode => {
143                    return Ok(IriOrBlankNode::BlankNode(value.to_string()))
144                }
145                _ => {}
146            }
147        }
148        Err(())
149    }
150}
151
152impl From<IriS> for MyTerm<'static> {
153    fn from(value: IriS) -> Self {
154        MyTerm::named_node(value.as_str(), 0..0).to_owned()
155    }
156}
157
158impl From<IriOrBlankNode> for MyTerm<'static> {
159    fn from(value: IriOrBlankNode) -> Self {
160        match value {
161            IriOrBlankNode::BlankNode(id) => MyTerm::blank_node(id, 0..0),
162            IriOrBlankNode::Iri(iri_s) => MyTerm::named_node(iri_s.as_str(), 0..0).to_owned(),
163        }
164    }
165}
166
167impl From<MyTerm<'static>> for IriS {
168    fn from(value: MyTerm<'static>) -> Self {
169        IriS::new_unchecked(value.as_str())
170    }
171}
172
173impl Iri for MyTerm<'static> {
174    fn as_str(&self) -> &str {
175        self.as_str()
176    }
177}
178
179impl From<MyQuad<'static>> for MyTerm<'static> {
180    fn from(value: MyQuad<'static>) -> Self {
181        tracing::error!(
182            "From<MyQuad<'static>> for MyTerm<'static> is not supported, using subject"
183        );
184        value.subject
185    }
186}
187
188impl Rudof_Term for MyTerm<'static> {
189    fn kind(&self) -> TermKind {
190        if let Some(ty) = self.ty {
191            map(ty)
192        } else {
193            TermKind::BlankNode
194        }
195    }
196
197    fn lexical_form(&self) -> String {
198        self.value.to_string()
199    }
200}
201
202impl BlankNode for MyTerm<'static> {
203    fn new(id: impl Into<String>) -> Self {
204        MyTerm::blank_node(id.into(), 0..0)
205    }
206
207    fn id(&self) -> &str {
208        &self.value
209    }
210}
211
212impl Literal for MyTerm<'static> {
213    fn lexical_form(&self) -> &str {
214        self.value.as_ref()
215    }
216
217    fn lang(&self) -> Option<r_term::literal::Lang> {
218        match &self.context {
219            TermContext::LangTag(cow) => return r_term::literal::Lang::new(cow.as_ref()).ok(),
220            _ => {}
221        }
222        None
223    }
224
225    fn datatype(&self) -> prefixmap::IriRef {
226        let str = match &self.context {
227            TermContext::DataType(cow) => cow.as_ref(),
228            _ => "http://www.w3.org/2001/XMLSchema#string",
229        };
230
231        prefixmap::IriRef::Iri(IriS::new_unchecked(str))
232    }
233
234    fn to_concrete_literal(&self) -> Option<r_term::literal::ConcreteLiteral> {
235        if self.ty == Some(sophia_api::term::TermKind::Literal) {
236            Some(self.clone().into())
237        } else {
238            None
239        }
240    }
241}
242
243impl From<r_term::Object> for MyTerm<'static> {
244    fn from(value: r_term::Object) -> Self {
245        use r_term::Object;
246        match value {
247            Object::Iri(iri_s) => MyTerm::named_node(iri_s.as_str(), 0..0).to_owned(),
248            Object::BlankNode(b) => MyTerm::blank_node(b, 0..0),
249            Object::Literal(sliteral) => sliteral.into(),
250            Object::Triple { subject, .. } => (*subject).into(),
251        }
252    }
253}
254
255fn dt_to_slit(value: &str, dt: &str) -> r_term::literal::ConcreteLiteral {
256    use r_term::literal::ConcreteLiteral as SLiteral;
257    if "http://www.w3.org/2001/XMLSchema#boolean" == dt {
258        if let Ok(b) = SLiteral::parse_bool(value) {
259            return SLiteral::BooleanLiteral(b);
260        }
261    }
262
263    if "http://www.w3.org/2001/XMLSchema#integer" == dt {
264        if let Ok(b) = SLiteral::parse_integer(value) {
265            return SLiteral::NumericLiteral(r_term::literal::NumericLiteral::Integer(b));
266        }
267    }
268
269    if "http://www.w3.org/2001/XMLSchema#double" == dt {
270        if let Ok(b) = SLiteral::parse_double(value) {
271            return SLiteral::NumericLiteral(r_term::literal::NumericLiteral::Double(b));
272        }
273    }
274
275    if "http://www.w3.org/2001/XMLSchema#decimal" == dt {
276        if let Ok(b) = SLiteral::parse_decimal(value) {
277            return SLiteral::NumericLiteral(r_term::literal::NumericLiteral::Decimal(b));
278        }
279    }
280
281    SLiteral::DatatypeLiteral {
282        lexical_form: value.to_string(),
283        datatype: prefixmap::IriRef::Iri(IriS::new_unchecked(&dt)),
284    }
285}
286
287impl From<MyTerm<'static>> for r_term::Object {
288    fn from(term: MyTerm<'static>) -> Self {
289        let MyTerm { ty, value, .. } = &term;
290        match ty.unwrap_or(sophia_api::prelude::TermKind::BlankNode) {
291            sophia_api::term::TermKind::Iri => {
292                r_term::Object::Iri(IriS::new_unchecked(value.as_ref()))
293            }
294            sophia_api::term::TermKind::BlankNode => r_term::Object::BlankNode(value.to_string()),
295            sophia_api::term::TermKind::Literal => r_term::Object::Literal(term.into()),
296            _ => {
297                tracing::error!(
298                    "Unsupported into srdf::Object for MyTerm with type {:?}",
299                    ty
300                );
301
302                r_term::Object::Iri(IriS::default())
303            }
304        }
305    }
306}
307
308impl From<r_term::literal::ConcreteLiteral> for MyTerm<'static> {
309    fn from(value: r_term::literal::ConcreteLiteral) -> Self {
310        let context = match value.lang() {
311            Some(dt) => TermContext::LangTag(Cow::Owned(dt.to_string())),
312            None => TermContext::DataType(Cow::Owned(value.datatype().to_string())),
313        };
314        MyTerm::literal(value.lexical_form(), 0..0, context)
315    }
316}
317
318impl From<bool> for MyTerm<'static> {
319    fn from(value: bool) -> Self {
320        MyTerm::literal(
321            value.to_string(),
322            0..0,
323            TermContext::DataType(Cow::Borrowed("http://www.w3.org/2001/XMLSchema#boolean")),
324        )
325    }
326}
327
328impl From<String> for MyTerm<'static> {
329    fn from(value: String) -> Self {
330        MyTerm::literal(
331            value,
332            0..0,
333            TermContext::DataType(Cow::Borrowed("http://www.w3.org/2001/XMLSchema#string")),
334        )
335    }
336}
337
338impl From<f64> for MyTerm<'static> {
339    fn from(value: f64) -> Self {
340        MyTerm::literal(
341            value.to_string(),
342            0..0,
343            TermContext::DataType(Cow::Borrowed("http://www.w3.org/2001/XMLSchema#double")),
344        )
345    }
346}
347
348impl From<i128> for MyTerm<'static> {
349    fn from(value: i128) -> Self {
350        MyTerm::literal(
351            value.to_string(),
352            0..0,
353            TermContext::DataType(Cow::Borrowed("http://www.w3.org/2001/XMLSchema#integer")),
354        )
355    }
356}
357
358impl From<MyTerm<'static>> for r_term::literal::ConcreteLiteral {
359    fn from(t: MyTerm<'static>) -> Self {
360        let value = t.value;
361        let context = t.context;
362        match context {
363            TermContext::None => r_term::literal::ConcreteLiteral::StringLiteral {
364                lexical_form: value.to_string(),
365                lang: None,
366            },
367            TermContext::DataType(dt) => dt_to_slit(value.as_ref(), dt.as_ref()),
368            TermContext::LangTag(cow) => r_term::literal::ConcreteLiteral::StringLiteral {
369                lexical_form: value.to_string(),
370                lang: r_term::literal::Lang::new(cow).ok(),
371            },
372        }
373    }
374}
375impl r_term::Triple<MyTerm<'static>, MyTerm<'static>, MyTerm<'static>> for MyQuad<'static> {
376    fn new(
377        subject: impl Into<MyTerm<'static>>,
378        predicate: impl Into<MyTerm<'static>>,
379        object: impl Into<MyTerm<'static>>,
380    ) -> Self {
381        MyQuad {
382            subject: subject.into(),
383            predicate: predicate.into(),
384            object: object.into(),
385            span: 0..0,
386        }
387    }
388
389    fn subj(&self) -> &MyTerm<'static> {
390        &self.subject
391    }
392
393    fn pred(&self) -> &MyTerm<'static> {
394        &self.predicate
395    }
396
397    fn obj(&self) -> &MyTerm<'static> {
398        &self.object
399    }
400
401    fn into_components(self) -> (MyTerm<'static>, MyTerm<'static>, MyTerm<'static>) {
402        let MyQuad {
403            subject,
404            predicate,
405            object,
406            ..
407        } = self;
408        (subject, predicate, object)
409    }
410}
411
412// impl From<IriOrBlankNode> for MyTerm<'static> {
413//     fn from(value: IriOrBlankNode) -> Self {
414//         todo!()
415//     }
416// }
417
418impl rudof_rdf::rdf_core::Rdf for Rdf {
419    type Subject = MyTerm<'static>;
420
421    type IRI = MyTerm<'static>;
422
423    type Term = MyTerm<'static>;
424
425    type BNode = MyTerm<'static>;
426
427    type Literal = MyTerm<'static>;
428
429    type Triple = MyQuad<'static>;
430
431    type Err = &'static str;
432
433    fn qualify_iri(&self, iri: &Self::IRI) -> String {
434        iri.value.to_string()
435    }
436
437    fn qualify_subject(&self, subj: &Self::Subject) -> String {
438        subj.value.to_string()
439    }
440
441    fn qualify_term(&self, term: &Self::Term) -> String {
442        term.value.to_string()
443    }
444
445    fn prefixmap(&self) -> Option<PrefixMap> {
446        None
447    }
448
449    fn resolve_prefix_local(
450        &self,
451        prefix: &str,
452        _local: &str,
453    ) -> std::result::Result<IriS, PrefixMapError> {
454        Err(PrefixMapError::PrefixNotFound {
455            prefix: prefix.to_string(),
456            prefixmap: PrefixMap::new(),
457        })
458    }
459
460    fn term_as_literal(term: &Self::Term) -> Result<Self::Literal, RDFError> {
461        if term.ty == Some(sophia_api::term::TermKind::Literal) {
462            <Self::Term as TryInto<Self::Literal>>::try_into(term.clone()).map_err(|_| {
463                RDFError::TermAsLiteral {
464                    term: term.to_string(),
465                }
466            })
467        } else {
468            Err(RDFError::TermAsLiteral {
469                term: term.to_string(),
470            })
471        }
472    }
473
474    fn term_as_sliteral(term: &Self::Term) -> Result<r_term::literal::ConcreteLiteral, RDFError> {
475        let lit = Self::term_as_literal(term)?;
476        let slit =
477            <Self::Literal as TryInto<r_term::literal::ConcreteLiteral>>::try_into(lit.clone())
478                .map_err(|_| RDFError::LiteralAsSLiteral {
479                    literal: lit.to_string(),
480                })?;
481        Ok(slit)
482    }
483
484    fn term_as_subject(term: &Self::Term) -> Result<Self::Subject, RDFError> {
485        if !(term.ty == Some(sophia_api::term::TermKind::BlankNode)
486            || term.ty == Some(sophia_api::term::TermKind::Iri))
487        {
488            return Err(RDFError::TermAsSubject {
489                term: term.to_string(),
490            });
491        }
492        <Self::Term as TryInto<Self::Subject>>::try_into(term.clone()).map_err(|_e| {
493            RDFError::TermAsSubject {
494                term: term.to_string(),
495            }
496        })
497    }
498
499    // Cannot use the default implementation because the types are the same between term and iri
500    // Which makes mapping impossible to fail
501    fn term_as_iri(term: &Self::Term) -> Result<Self::IRI, RDFError> {
502        if term.ty == Some(sophia_api::term::TermKind::Iri) {
503            <Self::Term as TryInto<Self::IRI>>::try_into(term.clone()).map_err(|_| {
504                RDFError::TermAsIri {
505                    term: term.to_string(),
506                }
507            })
508        } else {
509            Err(RDFError::TermAsIri {
510                term: term.to_string(),
511            })
512        }
513    }
514
515    fn term_as_iris(term: &Self::Term) -> Result<IriS, RDFError> {
516        if term.ty != Some(sophia_api::term::TermKind::Iri) {
517            return Err(RDFError::TermAsIriS {
518                term: term.to_string(),
519            });
520        }
521
522        let iri = <Self::Term as TryInto<Self::IRI>>::try_into(term.clone()).map_err(|_| {
523            RDFError::TermAsIriS {
524                term: term.to_string(),
525            }
526        })?;
527        let iri_s: IriS = iri.into();
528        Ok(iri_s)
529    }
530
531    fn term_as_bnode(term: &Self::Term) -> Result<Self::BNode, RDFError> {
532        if term.ty != Some(sophia_api::term::TermKind::BlankNode) {
533            return Err(RDFError::TermAsBNode {
534                term: term.to_string(),
535            });
536        }
537        <Self::Term as TryInto<Self::BNode>>::try_into(term.clone()).map_err(|_| {
538            RDFError::TermAsBNode {
539                term: term.to_string(),
540            }
541        })
542    }
543
544    fn compare(&self, term1: &Self::Term, term2: &Self::Term) -> Result<Ordering, RDFError> {
545        term1
546            .partial_cmp(&term2)
547            .ok_or_else(|| RDFError::ComparisonError {
548                term1: term1.to_string(),
549                term2: term2.to_string(),
550            })
551    }
552}
553
554fn map(term: sophia_api::term::TermKind) -> r_term::TermKind {
555    match term {
556        sophia_api::term::TermKind::Iri => TermKind::Iri,
557        sophia_api::term::TermKind::Literal => TermKind::Literal,
558        sophia_api::term::TermKind::BlankNode => TermKind::BlankNode,
559        sophia_api::term::TermKind::Triple => TermKind::Triple,
560        sophia_api::term::TermKind::Variable => TermKind::BlankNode,
561    }
562}
563
564impl r_term::Subject for MyTerm<'_> {
565    fn kind(&self) -> r_term::TermKind {
566        if let Some(ty) = self.ty {
567            map(ty)
568        } else {
569            TermKind::BlankNode
570        }
571    }
572}
573impl Matcher<MyTerm<'static>> for MyTerm<'static> {
574    fn value(&self) -> Option<&MyTerm<'static>> {
575        Some(self)
576    }
577}
578
579pub struct PrefixedPath<'a> {
580    pub path: &'a SHACLPath,
581    pub prefixes: &'a Prefixes,
582}
583impl<'a> PrefixedPath<'a> {
584    pub fn new(path: &'a SHACLPath, prefixes: &'a Prefixes) -> Self {
585        Self { path, prefixes }
586    }
587}
588
589impl<'a> Display for PrefixedPath<'a> {
590    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
591        match self.path {
592            SHACLPath::Predicate { pred } => {
593                if let Some(shorten) = self.prefixes.shorten(pred.as_str()) {
594                    write!(f, "{shorten}")
595                } else {
596                    write!(f, "{pred}")
597                }
598            }
599            SHACLPath::Alternative { paths } => {
600                write!(f, "(")?;
601                let mut first = true;
602                for p in paths {
603                    if !first {
604                        write!(f, " | ")?;
605                    }
606                    write!(f, "{}", Self::new(p, self.prefixes))?;
607                    first = false;
608                }
609                write!(f, ")")
610            }
611            SHACLPath::Sequence { paths } => {
612                write!(f, "(")?;
613                let mut first = true;
614                for p in paths {
615                    if !first {
616                        write!(f, " / ")?;
617                    }
618                    write!(f, "{}", Self::new(p, self.prefixes))?;
619                    first = false;
620                }
621                write!(f, ")")
622            }
623            SHACLPath::Inverse { path } => {
624                write!(f, "^({})", Self::new(path, self.prefixes))
625            }
626            SHACLPath::ZeroOrMore { path } => {
627                write!(f, "({})*", Self::new(path, self.prefixes))
628            }
629            SHACLPath::OneOrMore { path } => {
630                write!(f, "({})+", Self::new(path, self.prefixes))
631            }
632            SHACLPath::ZeroOrOne { path } => {
633                write!(f, "({})?", Self::new(path, self.prefixes))
634            }
635        }
636    }
637}