Skip to main content

swls_core/util/
triple.rs

1use std::{
2    borrow::{Borrow, Cow},
3    hash::Hash,
4    sync::Arc,
5    usize,
6};
7
8use bevy_ecs::prelude::*;
9use derive_more::{AsMut, AsRef, Deref, DerefMut};
10use sophia_api::{
11    prelude::{Any, Dataset},
12    quad::Quad,
13    term::{matcher::TermMatcher, BnodeId, GraphName, IriRef, Term, TermKind},
14    MownStr,
15};
16use tracing::{debug, instrument};
17
18use crate::{
19    components::{PositionComponent, RopeC},
20    util::{
21        ns::{owl, rdfs},
22        position_to_offset,
23    },
24};
25
26/// [`Component`] used to indicate the term type of currently targeted
27/// [`Token`](`crate::prelude::Token`) in the Triple.
28#[derive(Debug, PartialEq)]
29pub enum TripleTarget {
30    Subject,
31    Predicate,
32    Object,
33    Graph,
34}
35
36/// [`Component`] used to indicate the currently targeted [`MyQuad<'static>`] during a request.
37#[derive(Component, Debug)]
38pub struct TripleComponent {
39    pub triple: MyQuad<'static>,
40    pub target: TripleTarget,
41}
42
43impl TripleComponent {
44    pub fn kind(&self) -> TermKind {
45        let target = match self.target {
46            TripleTarget::Subject => self.triple.s().kind(),
47            TripleTarget::Predicate => self.triple.p().kind(),
48            TripleTarget::Object => self.triple.o().kind(),
49            TripleTarget::Graph => self
50                .triple
51                .g()
52                .map(|x| x.kind())
53                .unwrap_or(sophia_api::term::TermKind::Triple),
54        };
55        target
56    }
57
58    pub fn term(&self) -> Option<&MyTerm<'static>> {
59        let target = match self.target {
60            TripleTarget::Subject => self.triple.s(),
61            TripleTarget::Predicate => self.triple.p(),
62            TripleTarget::Object => self.triple.o(),
63            TripleTarget::Graph => return None,
64        };
65        Some(target)
66    }
67}
68
69/// [`Component`] containing all derived Triples from the documents.
70///
71/// These triples are used to derive properties and classes and other things.
72#[derive(Component, AsRef, Deref, AsMut, DerefMut, Debug)]
73pub struct Triples(pub Arc<Vec<MyQuad<'static>>>);
74
75impl Triples {
76    pub fn object<'s, S, P>(&'s self, subj: S, pred: P) -> Option<&'s MyTerm<'s>>
77    where
78        S: TermMatcher + 's,
79        P: TermMatcher + 's,
80    {
81        self.0
82            .quads_matching(
83                subj,
84                pred,
85                sophia_api::prelude::Any,
86                sophia_api::prelude::Any,
87            )
88            .flatten()
89            .next()
90            .map(|x| x.o())
91    }
92
93    pub fn objects<'s, S, P>(&'s self, subj: S, pred: P) -> impl Iterator<Item = &'s MyTerm<'s>>
94    where
95        S: TermMatcher + 's,
96        P: TermMatcher + 's,
97    {
98        self.0
99            .quads_matching(
100                subj,
101                pred,
102                sophia_api::prelude::Any,
103                sophia_api::prelude::Any,
104            )
105            .flatten()
106            .map(|x| x.o())
107    }
108}
109
110#[instrument(skip(query, commands))]
111pub fn get_current_triple(
112    query: Query<(Entity, &PositionComponent, &Triples, &RopeC)>,
113    mut commands: Commands,
114) {
115    for (e, position, triples, rope) in &query {
116        commands.entity(e).remove::<TripleComponent>();
117
118        let Some(offset) = position_to_offset(position.0, &rope.0) else {
119            debug!("Couldn't transform to an offset");
120            continue;
121        };
122
123        if let Some(t) = triples
124            .0
125            .iter()
126            .filter(|triple| triple.span.contains(&offset))
127            .min_by_key(|x| x.span.end - x.span.start)
128        {
129            let target = [
130                (TripleTarget::Subject, &t.subject.span),
131                (TripleTarget::Predicate, &t.predicate.span),
132                (TripleTarget::Object, &t.object.span),
133            ]
134            .into_iter()
135            .filter(|x| x.1.contains(&offset))
136            .min_by_key(|x| x.1.end - x.1.start)
137            .map(|x| x.0)
138            .unwrap_or(TripleTarget::Subject);
139
140            debug!("Current triple {} {:?}", t, target);
141            commands.entity(e).insert(TripleComponent {
142                triple: t.clone(),
143                target,
144            });
145        } else {
146            debug!("No current triple found");
147        }
148    }
149}
150
151#[derive(Debug, Clone)]
152pub struct MyQuad<'a> {
153    pub subject: MyTerm<'a>,
154    pub predicate: MyTerm<'a>,
155    pub object: MyTerm<'a>,
156    pub span: std::ops::Range<usize>,
157}
158impl<'b, 'a> TryFrom<&'b MyQuad<'a>> for oxigraph::model::Quad {
159    type Error = ();
160
161    fn try_from(value: &'b MyQuad<'a>) -> std::result::Result<Self, Self::Error> {
162        let subject = oxigraph::model::Term::try_from(&value.subject)?;
163        let predicate = oxigraph::model::Term::try_from(&value.predicate)?;
164        let object = oxigraph::model::Term::try_from(&value.object)?;
165
166        let subject = oxigraph::model::NamedOrBlankNode::try_from(subject).map_err(|_| ())?;
167        let predicate = oxigraph::model::NamedNode::try_from(predicate).map_err(|_| ())?;
168
169        Ok(oxigraph::model::Quad::new(
170            subject,
171            predicate,
172            object,
173            oxigraph::model::GraphName::default(),
174        ))
175    }
176}
177
178impl<'a> MyQuad<'a> {
179    pub fn into_oxi_graph(
180        &self,
181        graph: impl TryInto<oxigraph::model::Term>,
182    ) -> Result<oxigraph::model::Quad, ()> {
183        let graph = graph.try_into().map_err(|_| ())?;
184        let graph = oxigraph::model::NamedOrBlankNode::try_from(graph).map_err(|_| ())?;
185        let graph = oxigraph::model::GraphName::from(graph);
186
187        self.into_oxi(Some(graph))
188    }
189
190    pub fn into_oxi(
191        &self,
192        graph: Option<oxigraph::model::GraphName>,
193    ) -> Result<oxigraph::model::Quad, ()> {
194        let subject = oxigraph::model::Term::try_from(&self.subject)?;
195        let predicate = oxigraph::model::Term::try_from(&self.predicate)?;
196        let object = oxigraph::model::Term::try_from(&self.object)?;
197        let graph = graph.unwrap_or_default();
198
199        let subject = oxigraph::model::NamedOrBlankNode::try_from(subject).map_err(|_| ())?;
200        let predicate = oxigraph::model::NamedNode::try_from(predicate).map_err(|_| ())?;
201
202        Ok(oxigraph::model::Quad::new(
203            subject, predicate, object, graph,
204        ))
205    }
206}
207
208impl<'a> std::fmt::Display for MyQuad<'a> {
209    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
210        write!(
211            f,
212            "{} {} {}. # {:?}",
213            self.subject, self.predicate, self.object, self.span
214        )
215    }
216}
217
218impl<'a> MyQuad<'a> {
219    pub fn to_owned(&self) -> MyQuad<'static> {
220        MyQuad {
221            subject: self.subject.to_owned(),
222            predicate: self.predicate.to_owned(),
223            object: self.object.to_owned(),
224            span: self.span.clone(),
225        }
226    }
227}
228impl<'a, 'b> TryFrom<&'b MyTerm<'a>> for oxigraph::model::Term {
229    type Error = ();
230
231    fn try_from(value: &'b MyTerm<'a>) -> std::result::Result<Self, Self::Error> {
232        use oxigraph::model as M;
233        use oxigraph::model::Term as T;
234        let output = match &value.ty {
235            Some(TermKind::Iri) => T::NamedNode(M::NamedNode::new(value.as_str()).map_err(|_| ())?),
236            Some(TermKind::Literal) => {
237                if let Some(dt) = value.datatype() {
238                    let dt = M::NamedNode::new(dt.as_str()).map_err(|_| ())?;
239                    T::Literal(M::Literal::new_typed_literal(value.value.as_ref(), dt))
240                } else if let Some(lang) = value.language_tag() {
241                    T::Literal(
242                        M::Literal::new_language_tagged_literal(
243                            value.value.as_ref(),
244                            lang.as_str(),
245                        )
246                        .map_err(|_| ())?,
247                    )
248                } else {
249                    T::Literal(M::Literal::new_simple_literal(value.value.as_ref()))
250                }
251            }
252            Some(TermKind::BlankNode) => {
253                T::BlankNode(M::BlankNode::new(value.value.as_ref()).map_err(|_| ())?)
254            }
255            _ => {
256                return Err(());
257            }
258        };
259        return Ok(output);
260    }
261}
262
263impl<'a> Quad for MyQuad<'a> {
264    type Term = MyTerm<'a>;
265
266    fn s(&self) -> sophia_api::quad::QBorrowTerm<'_, Self> {
267        self.subject.borrow_term()
268    }
269
270    fn p(&self) -> sophia_api::quad::QBorrowTerm<'_, Self> {
271        self.predicate.borrow_term()
272    }
273
274    fn o(&self) -> sophia_api::quad::QBorrowTerm<'_, Self> {
275        self.object.borrow_term()
276    }
277
278    fn g(&self) -> GraphName<sophia_api::quad::QBorrowTerm<'_, Self>> {
279        None
280    }
281
282    fn to_spog(self) -> sophia_api::quad::Spog<Self::Term> {
283        ([self.subject, self.predicate, self.object], None)
284    }
285}
286// pub type MyQuad<'a> = ([MyTerm<'a>; 3], GraphName<MyTerm<'a>>);
287
288#[derive(Debug, Clone, PartialEq, Hash, Eq)]
289pub enum TermContext<'a> {
290    None,
291    DataType(Cow<'a, str>),
292    LangTag(Cow<'a, str>),
293}
294impl<'a> TermContext<'a> {
295    pub fn to_owned(&self) -> TermContext<'static> {
296        match self {
297            TermContext::None => TermContext::None,
298            TermContext::DataType(cow) => TermContext::DataType(Cow::Owned(cow.to_string())),
299            TermContext::LangTag(cow) => TermContext::LangTag(Cow::Owned(cow.to_string())),
300        }
301    }
302}
303
304#[derive(Debug, Clone, Eq)]
305pub struct MyTerm<'a> {
306    pub value: Cow<'a, str>,
307    pub ty: Option<TermKind>,
308    pub span: std::ops::Range<usize>,
309    pub context: TermContext<'a>,
310}
311impl<'a> PartialOrd for MyTerm<'a> {
312    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
313        match self.value.partial_cmp(&other.value) {
314            Some(core::cmp::Ordering::Equal) => {}
315            ord => return ord,
316        }
317        self.ty.partial_cmp(&other.ty)
318    }
319}
320impl<'a> Ord for MyTerm<'a> {
321    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
322        match self.value.cmp(&other.value) {
323            core::cmp::Ordering::Equal => {}
324            ord => return ord,
325        }
326        self.ty.cmp(&other.ty)
327    }
328}
329
330impl Hash for MyTerm<'_> {
331    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
332        // Ignore span
333        self.value.hash(state);
334        self.ty.hash(state);
335    }
336}
337
338impl PartialEq for MyTerm<'_> {
339    fn eq(&self, other: &Self) -> bool {
340        // Ignore span
341        other.value == self.value && other.ty == self.ty
342    }
343}
344
345impl<'a> std::fmt::Display for MyTerm<'a> {
346    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
347        match self.kind() {
348            TermKind::Iri => write!(f, "<{}>", self.value),
349            TermKind::Literal => write!(f, "\"{}\"", self.value),
350            TermKind::BlankNode => write!(f, "_:{}", self.value),
351            TermKind::Triple => write!(f, "<{}>", self.value),
352            TermKind::Variable => write!(f, "?{}", self.value),
353        }
354    }
355}
356
357impl<'a> MyTerm<'a> {
358    pub fn to_owned(&self) -> MyTerm<'static> {
359        let value = Cow::Owned(self.value.to_string());
360        MyTerm {
361            value,
362            ty: self.ty.clone(),
363            span: self.span.clone(),
364            context: self.context.to_owned(),
365        }
366    }
367    pub fn variable<T: Into<Cow<'a, str>>>(value: T, span: std::ops::Range<usize>) -> Self {
368        Self {
369            value: value.into(),
370            ty: TermKind::Variable.into(),
371            span,
372            context: TermContext::None,
373        }
374    }
375    pub fn named_node<T: Into<Cow<'a, str>>>(value: T, span: std::ops::Range<usize>) -> Self {
376        Self {
377            value: value.into(),
378            ty: TermKind::Iri.into(),
379            span,
380            context: TermContext::None,
381        }
382    }
383    pub fn blank_node<T: Into<Cow<'a, str>>>(value: T, span: std::ops::Range<usize>) -> Self {
384        Self {
385            value: value.into(),
386            ty: TermKind::BlankNode.into(),
387            span,
388            context: TermContext::None,
389        }
390    }
391    pub fn literal<T: Into<Cow<'a, str>>>(
392        value: T,
393        span: std::ops::Range<usize>,
394        context: TermContext<'a>,
395    ) -> Self {
396        Self {
397            value: value.into(),
398            ty: TermKind::Literal.into(),
399            span,
400            context,
401        }
402    }
403
404    pub fn invalid(span: std::ops::Range<usize>) -> Self {
405        Self {
406            value: Cow::default(),
407            ty: TermKind::Iri.into(),
408            span,
409
410            context: TermContext::None,
411        }
412    }
413
414    pub fn as_str(&'a self) -> &'a str {
415        &self.value
416    }
417}
418
419impl<'a> Term for MyTerm<'a> {
420    type BorrowTerm<'x>
421        = &'x Self
422    where
423        Self: 'x;
424
425    fn kind(&self) -> sophia_api::term::TermKind {
426        self.ty.unwrap_or(TermKind::Triple)
427    }
428
429    fn borrow_term(&self) -> Self::BorrowTerm<'_> {
430        self
431    }
432
433    fn iri(&self) -> Option<sophia_api::term::IriRef<sophia_api::MownStr<'_>>> {
434        self.is_iri()
435            .then(|| IriRef::new_unchecked(MownStr::from_ref(&self.value)))
436    }
437
438    fn bnode_id(&self) -> Option<sophia_api::term::BnodeId<sophia_api::MownStr<'_>>> {
439        self.is_blank_node()
440            .then(|| BnodeId::new_unchecked(MownStr::from_ref(&self.value)))
441    }
442
443    fn lexical_form(&self) -> Option<sophia_api::MownStr<'_>> {
444        self.is_literal().then(|| MownStr::from_ref(&self.value))
445    }
446
447    fn datatype(&self) -> Option<sophia_api::term::IriRef<sophia_api::MownStr<'_>>> {
448        None
449    }
450
451    fn language_tag(&self) -> Option<sophia_api::term::LanguageTag<sophia_api::MownStr<'_>>> {
452        None
453    }
454
455    fn variable(&self) -> Option<sophia_api::term::VarName<sophia_api::MownStr<'_>>> {
456        panic!("MyTerm does not supported variables")
457    }
458
459    fn triple(&self) -> Option<[Self::BorrowTerm<'_>; 3]> {
460        panic!("MyTerm does not supported triples")
461    }
462
463    fn to_triple(self) -> Option<[Self; 3]>
464    where
465        Self: Sized,
466    {
467        panic!("MyTerm does not supported triples")
468    }
469}
470
471impl<'a> Borrow<str> for &MyTerm<'a> {
472    fn borrow(&self) -> &str {
473        &self.value
474    }
475}
476
477#[derive(Default, Debug)]
478pub struct Triples2<'a> {
479    pub base_url: String,
480    pub triples: Vec<MyQuad<'a>>,
481    pub base: Option<MyTerm<'a>>,
482}
483
484impl<'a> Triples2<'a> {
485    pub fn to_owned(&self) -> Triples2<'static> {
486        let triples = self.triples.iter().map(|q| q.to_owned()).collect();
487        let base: Option<MyTerm<'static>> = self.base.as_ref().map(|x| x.to_owned());
488
489        Triples2 {
490            base,
491            triples,
492            base_url: self.base_url.clone(),
493        }
494    }
495
496    pub fn imports(&self, cb: impl FnMut(IriRef<MownStr<'_>>) -> ()) {
497        if let Some(ref base) = self.base {
498            self.triples
499                .quads_matching([base], [owl::imports], Any, Any)
500                .flatten()
501                .flat_map(|s| s.o().iri())
502                .for_each(cb);
503        }
504    }
505
506    pub fn sub_class_of(&self, mut cb: impl FnMut(IriRef<MownStr<'_>>, IriRef<MownStr<'_>>) -> ()) {
507        self.triples
508            .quads_matching(Any, [rdfs::subClassOf], Any, Any)
509            .flatten()
510            .flat_map(|s| match (s.s().iri(), s.o().iri()) {
511                (Some(s), Some(o)) => Some((s, o)),
512                _ => None,
513            })
514            .for_each(|(x, y)| cb(x, y));
515    }
516}
517
518impl<'a> std::ops::Deref for Triples2<'a> {
519    type Target = Vec<MyQuad<'a>>;
520
521    fn deref(&self) -> &Self::Target {
522        &self.triples
523    }
524}
525
526impl<'a> PartialEq for MyQuad<'a> {
527    fn eq(&self, other: &Self) -> bool {
528        self.subject == other.subject
529            && self.predicate == other.predicate
530            && self.object == other.object
531    }
532}