sophia_api/term/
_simple.rs

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