Skip to main content

sophia_api/term/
_simple.rs

1use super::*;
2use crate::ns::rdf;
3use crate::triple::Triple;
4
5lazy_static::lazy_static! {
6    static ref RDF_LANG_STRING: Box<str> = rdf::langString.iri().unwrap().unwrap().into();
7    static ref RDF_DIR_LANG_STRING: Box<str> = rdf::dirLangString.iri().unwrap().unwrap().into();
8}
9
10/// A straightforward implementation of [`Term`] as an enum.
11#[derive(Clone, Debug)]
12pub enum SimpleTerm<'a> {
13    /// An [RDF IRI](https://www.w3.org/TR/rdf12-concepts/#section-IRIs)
14    Iri(IriRef<MownStr<'a>>),
15    /// An RDF [blank node](https://www.w3.org/TR/rdf12-concepts/#section-blank-nodes)
16    BlankNode(BnodeId<MownStr<'a>>),
17    /// An RDF [literal](https://www.w3.org/TR/rdf12-concepts/#section-Graph-Literal)
18    LiteralDatatype(MownStr<'a>, IriRef<MownStr<'a>>),
19    /// An RDF [language-tagged string](https://www.w3.org/TR/rdf12-concepts/#dfn-language-tagged-string), potentially with base direction
20    LiteralLanguage(MownStr<'a>, LanguageTag<MownStr<'a>>, Option<BaseDirection>),
21    /// An RDF [triple term](https://www.w3.org/TR/rdf12-concepts/#section-blank-nodes)
22    Triple(Box<[Self; 3]>),
23    /// A SPARQL or Notation3 variable
24    Variable(VarName<MownStr<'a>>),
25}
26
27use SimpleTerm::*;
28
29impl<'a> Term for SimpleTerm<'a> {
30    type BorrowTerm<'x>
31        = &'x Self
32    where
33        'a: 'x;
34
35    fn kind(&self) -> TermKind {
36        match self {
37            Iri(_) => TermKind::Iri,
38            BlankNode(_) => TermKind::BlankNode,
39            LiteralDatatype(..) | LiteralLanguage(..) => TermKind::Literal,
40            Triple(_) => TermKind::Triple,
41            Variable(_) => TermKind::Variable,
42        }
43    }
44    fn iri(&self) -> Option<IriRef<MownStr<'_>>> {
45        if let Iri(iri) = self {
46            Some(IriRef::new_unchecked(iri.borrowed()))
47        } else {
48            None
49        }
50    }
51    fn bnode_id(&self) -> Option<BnodeId<MownStr<'_>>> {
52        if let BlankNode(bnid) = self {
53            Some(BnodeId::new_unchecked(bnid.borrowed()))
54        } else {
55            None
56        }
57    }
58    fn lexical_form(&self) -> Option<MownStr<'_>> {
59        match self {
60            LiteralDatatype(val, _) | LiteralLanguage(val, _, _) => Some(MownStr::from(&val[..])),
61            _ => None,
62        }
63    }
64    fn datatype(&self) -> Option<IriRef<MownStr<'_>>> {
65        match self {
66            LiteralDatatype(_, iri) => Some(IriRef::new_unchecked(iri.borrowed())),
67            LiteralLanguage(_, _, None) => {
68                Some(IriRef::new_unchecked(MownStr::from_ref(&RDF_LANG_STRING)))
69            }
70            LiteralLanguage(_, _, Some(_)) => Some(IriRef::new_unchecked(MownStr::from_ref(
71                &RDF_DIR_LANG_STRING,
72            ))),
73            _ => None,
74        }
75    }
76    fn language_tag(&self) -> Option<LanguageTag<MownStr<'_>>> {
77        if let LiteralLanguage(_, tag, _) = self {
78            Some(LanguageTag::new_unchecked(MownStr::from_ref(tag)))
79        } else {
80            None
81        }
82    }
83    fn base_direction(&self) -> Option<BaseDirection> {
84        if let LiteralLanguage(_, _, dir) = self {
85            *dir
86        } else {
87            None
88        }
89    }
90    fn variable(&self) -> Option<VarName<MownStr<'_>>> {
91        if let Variable(name) = self {
92            Some(VarName::new_unchecked(MownStr::from_ref(name)))
93        } else {
94            None
95        }
96    }
97    fn triple(&self) -> Option<[Self::BorrowTerm<'_>; 3]> {
98        if let Triple(triple) = self {
99            let [s, p, o] = triple.as_ref();
100            Some([s, p, o])
101        } else {
102            None
103        }
104    }
105    fn to_triple(self) -> Option<[Self; 3]> {
106        if let Triple(triple) = self {
107            Some(*triple)
108        } else {
109            None
110        }
111    }
112    fn borrow_term(&self) -> Self::BorrowTerm<'_> {
113        self
114    }
115}
116
117fn ensure_owned(m: MownStr) -> MownStr<'static> {
118    if m.is_owned() {
119        let m = m.clone();
120        // Safety: the transmute bellow is safe, because if m.is_owned() is true,
121        // then it's data is not restricted to lifetime 'a.
122        unsafe { std::mem::transmute::<mownstr::MownStr<'_>, mownstr::MownStr<'_>>(m) }
123    } else {
124        m.to_string().into()
125    }
126}
127
128impl FromTerm for SimpleTerm<'static> {
129    fn from_term<T: Term>(term: T) -> Self {
130        match term.kind() {
131            TermKind::Iri => SimpleTerm::Iri(term.iri().unwrap().map_unchecked(ensure_owned)),
132            TermKind::BlankNode => {
133                SimpleTerm::BlankNode(term.bnode_id().unwrap().map_unchecked(ensure_owned))
134            }
135            TermKind::Literal => {
136                let lex = ensure_owned(term.lexical_form().unwrap());
137                if let Some(tag) = term.language_tag() {
138                    let tag = tag.map_unchecked(ensure_owned);
139                    let dir = term.base_direction();
140                    SimpleTerm::LiteralLanguage(lex, tag, dir)
141                } else {
142                    let dt = term.datatype().unwrap().map_unchecked(ensure_owned);
143                    SimpleTerm::LiteralDatatype(lex, dt)
144                }
145            }
146            TermKind::Triple => {
147                let t = term.triple().unwrap();
148                SimpleTerm::Triple(Box::new([
149                    Self::from_term(t.s()),
150                    Self::from_term(t.p()),
151                    Self::from_term(t.o()),
152                ]))
153            }
154            TermKind::Variable => {
155                SimpleTerm::Variable(term.variable().unwrap().map_unchecked(ensure_owned))
156            }
157        }
158    }
159}
160
161impl TryFromTerm for SimpleTerm<'static> {
162    type Error = std::convert::Infallible;
163
164    fn try_from_term<T: Term>(term: T) -> Result<Self, Self::Error> {
165        Ok(Self::from_term(term))
166    }
167}
168
169impl<'a> SimpleTerm<'a> {
170    /// Build a [`SimpleTerm`] that borrows as much as possible from the original `term`.
171    ///
172    /// NB: depending on the implementation of `term`,
173    /// some data might still be allocated.
174    pub fn from_term_ref<T>(term: &'a T) -> Self
175    where
176        T: Term + ?Sized,
177    {
178        match term.kind() {
179            TermKind::Iri => SimpleTerm::Iri(term.iri().unwrap()),
180            TermKind::BlankNode => SimpleTerm::BlankNode(term.bnode_id().unwrap()),
181            TermKind::Literal => {
182                let lex = term.lexical_form().unwrap();
183                if let Some(tag) = term.language_tag() {
184                    let dir = term.base_direction();
185                    SimpleTerm::LiteralLanguage(lex, tag, dir)
186                } else {
187                    let dt = term.datatype().unwrap();
188                    SimpleTerm::LiteralDatatype(lex, dt)
189                }
190            }
191            TermKind::Triple => {
192                let t = term.triple().unwrap();
193                SimpleTerm::Triple(Box::new([
194                    SimpleTerm::<'static>::from_term(t.s()),
195                    SimpleTerm::<'static>::from_term(t.p()),
196                    SimpleTerm::<'static>::from_term(t.o()),
197                ]))
198            }
199            TermKind::Variable => SimpleTerm::Variable(term.variable().unwrap()),
200        }
201    }
202
203    /// Build a [`SimpleTerm`] of kind [`Triple`](TermKind::Triple) from any triple.
204    pub fn from_triple<T: crate::triple::Triple>(triple: T) -> Self {
205        Self::Triple(Box::new([
206            triple.s().into_term(),
207            triple.p().into_term(),
208            triple.o().into_term(),
209        ]))
210    }
211}
212
213impl<T: Term> PartialEq<T> for SimpleTerm<'_> {
214    fn eq(&self, other: &T) -> bool {
215        Term::eq(self, other.borrow_term())
216    }
217}
218
219impl Eq for SimpleTerm<'_> {}
220
221impl std::hash::Hash for SimpleTerm<'_> {
222    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
223        Term::hash(self, state)
224    }
225}
226
227impl<T: Term> PartialOrd<T> for SimpleTerm<'_> {
228    fn partial_cmp(&self, other: &T) -> Option<Ordering> {
229        Some(Term::cmp(self, other.borrow_term()))
230    }
231}
232
233impl Ord for SimpleTerm<'_> {
234    fn cmp(&self, other: &Self) -> Ordering {
235        Term::cmp(self, other)
236    }
237}
238
239#[cfg(test)]
240mod test {
241    use super::*;
242    use crate::ns::xsd;
243
244    #[test]
245    fn iri_from_scratch() {
246        let value = IriRef::new_unchecked(MownStr::from_ref("http://example.org/"));
247        let t = SimpleTerm::Iri(value.clone());
248        assert_consistent_term_impl(&t);
249        assert_eq!(t.borrow_term(), &t);
250        assert_eq!(t.kind(), TermKind::Iri);
251        assert_eq!(t.iri(), Some(value));
252    }
253
254    #[test]
255    fn bnode_from_scratch() {
256        let value = BnodeId::new_unchecked(MownStr::from_ref("b1"));
257        let t = SimpleTerm::BlankNode(value.clone());
258        assert_consistent_term_impl(&t);
259        assert_eq!(t.borrow_term(), &t);
260        assert_eq!(t.kind(), TermKind::BlankNode);
261        assert_eq!(t.bnode_id(), Some(value));
262    }
263
264    #[test]
265    fn literal_dt_from_scratch() {
266        let value = MownStr::from_ref("hello world");
267        let datatype = IriRef::new_unchecked(MownStr::from_ref("http://example.org/"));
268        let t = SimpleTerm::LiteralDatatype(value.clone(), datatype.clone());
269        assert_consistent_term_impl(&t);
270        assert_eq!(t.borrow_term(), &t);
271        assert_eq!(t.kind(), TermKind::Literal);
272        assert_eq!(t.lexical_form(), Some(value));
273        assert_eq!(t.datatype(), Some(datatype));
274    }
275
276    #[test]
277    fn literal_lang_from_scratch() {
278        let value = MownStr::from_ref("hello world");
279        let tag = LanguageTag::new_unchecked(MownStr::from_ref("en-US"));
280        let t = SimpleTerm::LiteralLanguage(value.clone(), tag.clone(), None);
281        assert_consistent_term_impl(&t);
282        assert_eq!(t.borrow_term(), &t);
283        assert_eq!(t.kind(), TermKind::Literal);
284        assert_eq!(t.lexical_form(), Some(value));
285        assert_eq!(t.language_tag(), Some(tag));
286        assert_eq!(t.base_direction(), None);
287    }
288
289    #[test]
290    fn literal_dir_lang_from_scratch() {
291        let value = MownStr::from_ref("hello world");
292        let tag = LanguageTag::new_unchecked(MownStr::from_ref("en-US"));
293        let dir = BaseDirection::Ltr;
294        let t = SimpleTerm::LiteralLanguage(value.clone(), tag.clone(), Some(dir));
295        assert_consistent_term_impl(&t);
296        assert_eq!(t.borrow_term(), &t);
297        assert_eq!(t.kind(), TermKind::Literal);
298        assert_eq!(t.lexical_form(), Some(value));
299        assert_eq!(t.language_tag(), Some(tag));
300        assert_eq!(t.base_direction(), Some(dir));
301    }
302
303    #[test]
304    fn variable_from_scratch() {
305        let value = VarName::new_unchecked(MownStr::from_ref("x"));
306        let t = SimpleTerm::Variable(value.clone());
307        assert_consistent_term_impl(&t);
308        assert_eq!(t.borrow_term(), &t);
309        assert_eq!(t.kind(), TermKind::Variable);
310        assert_eq!(t.variable(), Some(value));
311    }
312
313    #[test]
314    fn triple_from_scratch() {
315        let s: SimpleTerm<'_> = BnodeId::new_unchecked(MownStr::from_ref("s")).into_term();
316        let p: SimpleTerm<'_> = IriRef::new_unchecked(MownStr::from_ref("p")).into_term();
317        let o: SimpleTerm<'_> = "o".into_term();
318        let spo = [s.clone(), p.clone(), o.clone()];
319        let t = SimpleTerm::Triple(Box::new(spo.clone()));
320        assert_consistent_term_impl(&t);
321        assert_eq!(t.borrow_term(), &t);
322        assert_eq!(t.kind(), TermKind::Triple);
323        assert_eq!(t.triple(), Some([&s, &p, &o]));
324        assert_eq!(t.clone().to_triple(), Some(spo.clone()));
325        assert_eq!(t.constituents().collect::<Vec<_>>(), vec![&t, &s, &p, &o]);
326        assert_eq!(
327            t.clone().to_constituents().collect::<Vec<_>>(),
328            t.constituents().cloned().collect::<Vec<_>>()
329        );
330        assert_eq!(t.atoms().collect::<Vec<_>>(), vec![&s, &p, &o]);
331        assert_eq!(
332            t.clone().to_atoms().collect::<Vec<_>>(),
333            Vec::from(spo.clone())
334        );
335    }
336
337    #[test]
338    fn nested_triple_from_scratch() {
339        let s1: SimpleTerm<'_> = BnodeId::new_unchecked(MownStr::from_ref("s")).into_term();
340        let p1: SimpleTerm<'_> = IriRef::new_unchecked(MownStr::from_ref("p")).into_term();
341        let o1: SimpleTerm<'_> = "o".into_term();
342        let spo1 = [s1.clone(), p1.clone(), o1.clone()];
343        let t1 = SimpleTerm::Triple(Box::new(spo1));
344        let s: SimpleTerm<'_> = s1.clone();
345        let p: SimpleTerm<'_> = p1.clone();
346        let o = t1.clone();
347        let spo2 = [s.clone(), p.clone(), o.clone()];
348        let t = SimpleTerm::Triple(Box::new(spo2.clone()));
349        assert_consistent_term_impl(&t);
350        assert_eq!(t.borrow_term(), &t);
351        assert_eq!(t.kind(), TermKind::Triple);
352        assert_eq!(t.triple(), Some([&s, &p, &o]));
353        assert_eq!(t.clone().to_triple(), Some(spo2.clone()));
354        assert_eq!(
355            t.constituents().collect::<Vec<_>>(),
356            vec![&t, &s, &p, &t1, &s1, &p1, &o1]
357        );
358        assert_eq!(
359            t.clone().to_constituents().collect::<Vec<_>>(),
360            t.constituents().cloned().collect::<Vec<_>>()
361        );
362        assert_eq!(t.atoms().collect::<Vec<_>>(), vec![&s, &p, &s1, &p1, &o1]);
363        assert_eq!(
364            t.clone().to_atoms().collect::<Vec<_>>(),
365            t.atoms().cloned().collect::<Vec<_>>()
366        );
367    }
368
369    #[test]
370    fn iri_from_term() {
371        let t: SimpleTerm<'_> = rdf::type_.into_term();
372        assert_consistent_term_impl(&t);
373        assert_eq!(t.kind(), TermKind::Iri);
374        assert_eq!(t.iri(), rdf::type_.iri());
375    }
376
377    #[test]
378    fn literal_from_term() {
379        let t: SimpleTerm<'_> = "hello world".into_term();
380        assert_consistent_term_impl(&t);
381        assert_eq!(t.kind(), TermKind::Literal);
382        assert_eq!(t.lexical_form().unwrap(), "hello world");
383        assert_eq!(t.datatype(), xsd::string.iri());
384
385        let t: SimpleTerm<'_> = 42.into_term();
386        assert_consistent_term_impl(&t);
387        assert_eq!(t.kind(), TermKind::Literal);
388        assert_eq!(t.lexical_form().unwrap(), "42");
389        assert_eq!(t.datatype(), xsd::integer.iri());
390    }
391
392    #[test]
393    fn bnode_from_term() {
394        let b1 = BnodeId::new("b1").unwrap();
395        let t: SimpleTerm<'_> = b1.into_term();
396        assert_consistent_term_impl(&t);
397        assert_eq!(t.kind(), TermKind::BlankNode);
398        assert_eq!(t.bnode_id().unwrap(), b1);
399    }
400
401    #[test]
402    fn triple_from_term() {
403        let s: SimpleTerm<'_> = BnodeId::new_unchecked(MownStr::from_ref("s")).into_term();
404        let p: SimpleTerm<'_> = IriRef::new_unchecked(MownStr::from_ref("p")).into_term();
405        let o: SimpleTerm<'_> = "o".into_term();
406        let spo = [s.clone(), p.clone(), o.clone()];
407        let tr = SimpleTerm::from_triple(spo.spo());
408        let t: SimpleTerm<'_> = tr.into_term();
409        assert_consistent_term_impl(&t);
410        assert_eq!(t.borrow_term(), &t);
411        assert_eq!(t.kind(), TermKind::Triple);
412        assert_eq!(t.triple(), Some([&s, &p, &o]));
413        assert_eq!(t.clone().to_triple(), Some(spo.clone()));
414        assert_eq!(t.constituents().collect::<Vec<_>>(), vec![&t, &s, &p, &o]);
415        assert_eq!(
416            t.clone().to_constituents().collect::<Vec<_>>(),
417            t.constituents().cloned().collect::<Vec<_>>()
418        );
419        assert_eq!(t.atoms().collect::<Vec<_>>(), vec![&s, &p, &o]);
420        assert_eq!(
421            t.clone().to_atoms().collect::<Vec<_>>(),
422            Vec::from(spo.clone())
423        );
424    }
425
426    #[test]
427    fn variable_from_term() {
428        let v1 = VarName::new("v1").unwrap();
429        let t: SimpleTerm<'_> = v1.into_term();
430        assert_consistent_term_impl(&t);
431        assert_eq!(t.kind(), TermKind::Variable);
432        assert_eq!(t.variable().unwrap(), v1);
433    }
434
435    #[test]
436    fn try_from_term() {
437        let t: SimpleTerm<'_> = 42.try_into_term().unwrap();
438        assert_consistent_term_impl(&t);
439        assert_eq!(t.kind(), TermKind::Literal);
440        assert_eq!(t.lexical_form().unwrap(), "42");
441        assert_eq!(t.datatype(), xsd::integer.iri());
442    }
443
444    #[test]
445    fn iri_from_term_ref() {
446        let i = sophia_iri::Iri::new("http://example.com/").unwrap();
447        let t = SimpleTerm::from_term_ref(&i);
448        assert_consistent_term_impl(&t);
449        assert_eq!(t.kind(), TermKind::Iri);
450        assert_eq!(t.iri(), i.iri());
451        assert!(t.iri().unwrap().unwrap().is_borrowed());
452    }
453
454    #[test]
455    fn literal_from_term_ref() {
456        let l = "hello world";
457        let t = SimpleTerm::from_term_ref(&l);
458        assert_consistent_term_impl(&t);
459        assert_eq!(t.kind(), TermKind::Literal);
460        assert_eq!(t.lexical_form().unwrap(), l);
461        assert!(t.lexical_form().unwrap().is_borrowed());
462    }
463
464    #[test]
465    fn bnode_from_term_ref() {
466        let b = BnodeId::new("b1").unwrap();
467        let t = SimpleTerm::from_term_ref(&b);
468        assert_consistent_term_impl(&t);
469        assert_eq!(t.kind(), TermKind::BlankNode);
470        assert_eq!(t.bnode_id().unwrap(), b);
471        assert!(t.bnode_id().unwrap().unwrap().is_borrowed());
472    }
473
474    #[test]
475    fn triple_from_term_ref() {
476        let s: SimpleTerm<'_> = BnodeId::new_unchecked(MownStr::from_ref("s")).into_term();
477        let p: SimpleTerm<'_> = IriRef::new_unchecked(MownStr::from_ref("p")).into_term();
478        let o: SimpleTerm<'_> = "o".into_term();
479        let spo = [s.clone(), p.clone(), o.clone()];
480        let tr = SimpleTerm::from_triple(spo.spo());
481        let t = SimpleTerm::from_term_ref(&tr);
482        assert_consistent_term_impl(&t);
483        assert_eq!(t.kind(), TermKind::Triple);
484        let inner_s = t.to_atoms().next().unwrap();
485        assert!(inner_s.bnode_id().unwrap().unwrap().is_borrowed());
486    }
487
488    #[test]
489    fn variable_from_term_ref() {
490        let v = VarName::new("v1").unwrap();
491        let t = SimpleTerm::from_term_ref(&v);
492        assert_consistent_term_impl(&t);
493        assert_eq!(t.kind(), TermKind::Variable);
494        assert_eq!(t.variable().unwrap(), v);
495        assert!(t.variable().unwrap().unwrap().is_borrowed());
496    }
497}