sophia_api/term/
_native_literal.rs

1use super::*;
2use crate::ns::xsd;
3
4lazy_static::lazy_static! {
5    static ref XSD_DOUBLE: Box<str> = xsd::double.iri().unwrap().unwrap().into();
6    static ref XSD_INTEGER: Box<str> = xsd::integer.iri().unwrap().unwrap().into();
7    static ref XSD_STRING: Box<str> = xsd::string.iri().unwrap().unwrap().into();
8    static ref XSD_BOOLEAN: Box<str> = xsd::boolean.iri().unwrap().unwrap().into();
9}
10
11/// [`f64`] implements [`Term`]
12/// so that Rust literals can be used as RDF literals in code.
13///
14/// E.g.:
15/// ```
16/// # use sophia_api::graph::{MutableGraph, Graph};
17/// # use sophia_api::term::SimpleTerm;
18/// # use sophia_api::ns::{rdf, rdfs};
19/// # use sophia_iri::IriRef;
20/// # fn test<T: MutableGraph>(graph: &mut T) -> Result<(), Box<dyn std::error::Error>> {
21/// # let subject: IriRef<&'static str> = IriRef::new("")?;
22/// #
23/// graph.insert(&subject, &rdf::value, 3.14)?;
24/// #
25/// # Ok(()) }
26/// ```
27impl Term for f64 {
28    type BorrowTerm<'x> = Self;
29
30    fn kind(&self) -> TermKind {
31        TermKind::Literal
32    }
33    fn lexical_form(&self) -> Option<MownStr> {
34        Some(MownStr::from(format!("{}", self)))
35    }
36    fn datatype(&self) -> Option<IriRef<MownStr>> {
37        Some(IriRef::new_unchecked(MownStr::from_ref(&XSD_DOUBLE)))
38    }
39    fn language_tag(&self) -> Option<LanguageTag<MownStr>> {
40        None
41    }
42    fn borrow_term(&self) -> Self::BorrowTerm<'_> {
43        *self
44    }
45}
46
47/// [`i32`] implements [`Term`]
48/// so that Rust literals can be used as RDF literals in code.
49///
50/// E.g.:
51/// ```
52/// # use sophia_api::graph::{MutableGraph, Graph};
53/// # use sophia_api::term::SimpleTerm;
54/// # use sophia_api::ns::{rdf, rdfs};
55/// # use sophia_iri::IriRef;
56/// # fn test<T: MutableGraph>(graph: &mut T) -> Result<(), Box<dyn std::error::Error>> {
57/// # let subject: IriRef<&'static str> = IriRef::new("")?;
58/// #
59/// graph.insert(&subject, &rdf::value, 42)?;
60/// #
61/// # Ok(()) }
62/// ```
63impl Term for i32 {
64    type BorrowTerm<'x> = Self;
65
66    fn kind(&self) -> TermKind {
67        TermKind::Literal
68    }
69    fn lexical_form(&self) -> Option<MownStr> {
70        Some(MownStr::from(format!("{}", self)))
71    }
72    fn datatype(&self) -> Option<IriRef<MownStr>> {
73        Some(IriRef::new_unchecked(MownStr::from_ref(&XSD_INTEGER)))
74    }
75    fn language_tag(&self) -> Option<LanguageTag<MownStr>> {
76        None
77    }
78    fn borrow_term(&self) -> Self::BorrowTerm<'_> {
79        *self
80    }
81}
82
83/// [`isize`] implements [`Term`]
84/// so that Rust values can be used as RDF literals in code.
85///
86/// E.g.:
87/// ```
88/// # use sophia_api::graph::{MutableGraph, Graph};
89/// # use sophia_api::term::SimpleTerm;
90/// # use sophia_api::ns::{rdf, rdfs};
91/// # use sophia_iri::IriRef;
92/// # fn test<T: MutableGraph>(graph: &mut T) -> Result<(), Box<dyn std::error::Error>> {
93/// # let subject: IriRef<&'static str> = IriRef::new("")?;
94/// #
95/// let answer: isize = 42;
96/// graph.insert(&subject, &rdf::value, answer)?;
97/// #
98/// # Ok(()) }
99/// ```
100impl Term for isize {
101    type BorrowTerm<'x> = Self;
102
103    fn kind(&self) -> TermKind {
104        TermKind::Literal
105    }
106    fn lexical_form(&self) -> Option<MownStr> {
107        Some(MownStr::from(format!("{}", self)))
108    }
109    fn datatype(&self) -> Option<IriRef<MownStr>> {
110        Some(IriRef::new_unchecked(MownStr::from_ref(&XSD_INTEGER)))
111    }
112    fn language_tag(&self) -> Option<LanguageTag<MownStr>> {
113        None
114    }
115    fn borrow_term(&self) -> Self::BorrowTerm<'_> {
116        *self
117    }
118}
119
120/// [`usize`] implements [`Term`]
121/// so that Rust values can be used as RDF literals in code.
122///
123/// E.g.:
124/// ```
125/// # use sophia_api::graph::{MutableGraph, Graph};
126/// # use sophia_api::term::SimpleTerm;
127/// # use sophia_api::ns::{rdf, rdfs};
128/// # use sophia_iri::IriRef;
129/// # fn test<T: MutableGraph>(graph: &mut T) -> Result<(), Box<dyn std::error::Error>> {
130/// # let subject: IriRef<&'static str> = IriRef::new("")?;
131/// #
132/// let answer: usize = 42;
133/// graph.insert(&subject, &rdf::value, answer)?;
134/// #
135/// # Ok(()) }
136/// ```
137impl Term for usize {
138    type BorrowTerm<'x> = Self;
139
140    fn kind(&self) -> TermKind {
141        TermKind::Literal
142    }
143    fn lexical_form(&self) -> Option<MownStr> {
144        Some(MownStr::from(format!("{}", self)))
145    }
146    fn datatype(&self) -> Option<IriRef<MownStr>> {
147        Some(IriRef::new_unchecked(MownStr::from_ref(&XSD_INTEGER)))
148    }
149    fn language_tag(&self) -> Option<LanguageTag<MownStr>> {
150        None
151    }
152    fn borrow_term(&self) -> Self::BorrowTerm<'_> {
153        *self
154    }
155}
156
157/// [`str`] implements [`Term`]
158/// so that Rust literals can be used as RDF literals in code.
159///
160/// E.g.:
161/// ```
162/// # use sophia_api::graph::{MutableGraph, Graph};
163/// # use sophia_api::term::SimpleTerm;
164/// # use sophia_api::ns::{rdf, rdfs};
165/// # use sophia_iri::IriRef;
166/// # fn test<T: MutableGraph>(graph: &mut T) -> Result<(), Box<dyn std::error::Error>> {
167/// # let subject: IriRef<&'static str> = IriRef::new("")?;
168/// #
169/// graph.insert(&subject, &rdfs::label, "hello world")?;
170/// #
171/// # Ok(()) }
172/// ```
173impl Term for str {
174    type BorrowTerm<'x> = &'x Self where Self: 'x;
175
176    fn kind(&self) -> TermKind {
177        TermKind::Literal
178    }
179    fn lexical_form(&self) -> Option<MownStr> {
180        Some(MownStr::from(self))
181    }
182    fn datatype(&self) -> Option<IriRef<MownStr>> {
183        Some(IriRef::new_unchecked(MownStr::from_ref(&XSD_STRING)))
184    }
185    fn language_tag(&self) -> Option<LanguageTag<MownStr>> {
186        None
187    }
188    fn borrow_term(&self) -> Self::BorrowTerm<'_> {
189        self
190    }
191}
192
193/// [`bool`] implements [`Term`]
194/// so that Rust literals can be used as RDF literals in code.
195///
196/// E.g.:
197/// ```
198/// # use sophia_api::graph::{MutableGraph, Graph};
199/// # use sophia_api::term::SimpleTerm;
200/// # use sophia_api::ns::{rdf, rdfs};
201/// # use sophia_iri::IriRef;
202/// # fn test<T: MutableGraph>(graph: &mut T) -> Result<(), Box<dyn std::error::Error>> {
203/// # let subject: IriRef<&'static str> = IriRef::new("")?;
204/// #
205/// graph.insert(&subject, &rdf::value, true)?;
206/// #
207/// # Ok(()) }
208/// ```
209impl Term for bool {
210    type BorrowTerm<'x> = Self;
211
212    fn kind(&self) -> TermKind {
213        TermKind::Literal
214    }
215    fn lexical_form(&self) -> Option<MownStr> {
216        Some(MownStr::from(if *self { "true" } else { "false" }))
217    }
218    fn datatype(&self) -> Option<IriRef<MownStr>> {
219        Some(IriRef::new_unchecked(MownStr::from_ref(&XSD_BOOLEAN)))
220    }
221    fn language_tag(&self) -> Option<LanguageTag<MownStr>> {
222        None
223    }
224    fn borrow_term(&self) -> Self::BorrowTerm<'_> {
225        *self
226    }
227}
228
229/// [`f64`] implements [`TryFromTerm`]
230/// so that compatible datatypes can easily be converted to native Rust values.
231impl TryFromTerm for f64 {
232    type Error = std::num::ParseFloatError;
233
234    fn try_from_term<T: Term>(term: T) -> Result<Self, Self::Error> {
235        if let Some(lex) = term.lexical_form() {
236            if Term::eq(&term.datatype().unwrap(), xsd::double)
237                || Term::eq(&term.datatype().unwrap(), xsd::float)
238                || Term::eq(&term.datatype().unwrap(), xsd::decimal)
239            {
240                lex.parse()
241            } else {
242                "wrong datatype".parse()
243            }
244        } else {
245            "not a literal".parse()
246        }
247    }
248}
249
250/// [`i32`] implements [`TryFromTerm`]
251/// so that compatible datatypes can easily be converted to native Rust values.
252impl TryFromTerm for i32 {
253    type Error = std::num::ParseIntError;
254
255    fn try_from_term<T: Term>(term: T) -> Result<Self, Self::Error> {
256        if let Some(lex) = term.lexical_form() {
257            if Term::eq(&term.datatype().unwrap(), xsd::integer)
258                || Term::eq(&term.datatype().unwrap(), xsd::long)
259                || Term::eq(&term.datatype().unwrap(), xsd::int)
260                || Term::eq(&term.datatype().unwrap(), xsd::short)
261                || Term::eq(&term.datatype().unwrap(), xsd::unsignedLong)
262                || Term::eq(&term.datatype().unwrap(), xsd::unsignedInt)
263                || Term::eq(&term.datatype().unwrap(), xsd::unsignedShort)
264                || Term::eq(&term.datatype().unwrap(), xsd::unsignedByte)
265                || Term::eq(&term.datatype().unwrap(), xsd::nonNegativeInteger)
266                || Term::eq(&term.datatype().unwrap(), xsd::nonPositiveInteger)
267                || Term::eq(&term.datatype().unwrap(), xsd::negativeInteger)
268                || Term::eq(&term.datatype().unwrap(), xsd::positiveInteger)
269            {
270                lex.parse()
271            } else {
272                "wrong datatype".parse()
273            }
274        } else {
275            "not a literal".parse()
276        }
277    }
278}
279
280/// [`isize`] implements [`TryFromTerm`]
281/// so that compatible datatypes can easily be converted to native Rust values.
282impl TryFromTerm for isize {
283    type Error = std::num::ParseIntError;
284
285    fn try_from_term<T: Term>(term: T) -> Result<Self, Self::Error> {
286        if let Some(lex) = term.lexical_form() {
287            if Term::eq(&term.datatype().unwrap(), xsd::integer)
288                || Term::eq(&term.datatype().unwrap(), xsd::long)
289                || Term::eq(&term.datatype().unwrap(), xsd::int)
290                || Term::eq(&term.datatype().unwrap(), xsd::short)
291                || Term::eq(&term.datatype().unwrap(), xsd::unsignedLong)
292                || Term::eq(&term.datatype().unwrap(), xsd::unsignedInt)
293                || Term::eq(&term.datatype().unwrap(), xsd::unsignedShort)
294                || Term::eq(&term.datatype().unwrap(), xsd::unsignedByte)
295                || Term::eq(&term.datatype().unwrap(), xsd::nonNegativeInteger)
296                || Term::eq(&term.datatype().unwrap(), xsd::nonPositiveInteger)
297                || Term::eq(&term.datatype().unwrap(), xsd::negativeInteger)
298                || Term::eq(&term.datatype().unwrap(), xsd::positiveInteger)
299            {
300                lex.parse()
301            } else {
302                "wrong datatype".parse()
303            }
304        } else {
305            "not a literal".parse()
306        }
307    }
308}
309
310/// [`usize`] implements [`TryFromTerm`]
311/// so that compatible datatypes can easily be converted to native Rust values.
312impl TryFromTerm for usize {
313    type Error = std::num::ParseIntError;
314
315    fn try_from_term<T: Term>(term: T) -> Result<Self, Self::Error> {
316        if let Some(lex) = term.lexical_form() {
317            if Term::eq(&term.datatype().unwrap(), xsd::integer)
318                || Term::eq(&term.datatype().unwrap(), xsd::long)
319                || Term::eq(&term.datatype().unwrap(), xsd::int)
320                || Term::eq(&term.datatype().unwrap(), xsd::short)
321                || Term::eq(&term.datatype().unwrap(), xsd::unsignedLong)
322                || Term::eq(&term.datatype().unwrap(), xsd::unsignedInt)
323                || Term::eq(&term.datatype().unwrap(), xsd::unsignedShort)
324                || Term::eq(&term.datatype().unwrap(), xsd::unsignedByte)
325                || Term::eq(&term.datatype().unwrap(), xsd::nonNegativeInteger)
326                || Term::eq(&term.datatype().unwrap(), xsd::positiveInteger)
327            {
328                lex.parse()
329            } else {
330                "wrong datatype".parse()
331            }
332        } else {
333            "not a literal".parse()
334        }
335    }
336}
337
338/// [`bool`] implements [`TryFromTerm`]
339/// so that compatible datatypes can easily be converted to native Rust values.
340impl TryFromTerm for bool {
341    type Error = std::str::ParseBoolError;
342
343    fn try_from_term<T: Term>(term: T) -> Result<Self, Self::Error> {
344        if let Some(lex) = term.lexical_form() {
345            if Term::eq(&term.datatype().unwrap(), xsd::boolean) {
346                lex.parse()
347            } else {
348                "wrong datatype".parse()
349            }
350        } else {
351            "not a literal".parse()
352        }
353    }
354}
355
356#[cfg(test)]
357mod test {
358    use super::*;
359
360    #[test]
361    fn i32_as_literal() {
362        let lit = 42;
363        assert_consistent_term_impl::<i32>(&lit);
364        assert_eq!(lit.kind(), TermKind::Literal);
365        assert_eq!(lit.lexical_form().unwrap(), "42");
366        assert_eq!(lit.datatype(), xsd::integer.iri());
367        assert_eq!(lit.borrow_term(), lit);
368    }
369
370    #[test]
371    fn isize_as_literal() {
372        let lit = 42;
373        assert_consistent_term_impl::<isize>(&lit);
374        assert_eq!(lit.kind(), TermKind::Literal);
375        assert_eq!(lit.lexical_form().unwrap(), "42");
376        assert_eq!(lit.datatype(), xsd::integer.iri());
377        assert_eq!(lit.borrow_term(), lit);
378    }
379
380    #[test]
381    fn usize_as_literal() {
382        let lit = 42;
383        assert_consistent_term_impl::<usize>(&lit);
384        assert_eq!(lit.kind(), TermKind::Literal);
385        assert_eq!(lit.lexical_form().unwrap(), "42");
386        assert_eq!(lit.datatype(), xsd::integer.iri());
387        assert_eq!(lit.borrow_term(), lit);
388    }
389
390    #[test]
391    fn f64_as_literal() {
392        #[allow(clippy::approx_constant)]
393        let lit = 3.14;
394        assert_consistent_term_impl::<f64>(&lit);
395        assert_eq!(lit.kind(), TermKind::Literal);
396        assert_eq!(lit.lexical_form().unwrap(), "3.14");
397        assert_eq!(lit.datatype(), xsd::double.iri());
398        assert_eq!(lit.borrow_term(), lit);
399    }
400
401    #[test]
402    fn str_as_literal() {
403        let lit = "hello world";
404        assert_consistent_term_impl::<&str>(&lit);
405        assert_eq!(lit.kind(), TermKind::Literal);
406        assert_eq!(lit.lexical_form().unwrap(), lit);
407        assert_eq!(lit.datatype(), xsd::string.iri());
408        assert_eq!(lit.borrow_term(), lit);
409    }
410
411    #[test]
412    fn bool_as_literal() {
413        let lit = false;
414        assert_consistent_term_impl::<bool>(&lit);
415        assert_eq!(lit.kind(), TermKind::Literal);
416        assert_eq!(lit.lexical_form().unwrap(), "false");
417        assert_eq!(lit.datatype(), xsd::boolean.iri());
418        assert_eq!(lit.borrow_term(), lit);
419    }
420
421    #[test]
422    fn iri_to_native() {
423        assert!(f64::try_from_term(xsd::ID).is_err());
424        assert!(i32::try_from_term(xsd::ID).is_err());
425        assert!(isize::try_from_term(xsd::ID).is_err());
426        assert!(usize::try_from_term(xsd::ID).is_err());
427    }
428
429    #[test]
430    fn wrong_datatype_to_native() {
431        assert!(f64::try_from_term("foo").is_err());
432        assert!(i32::try_from_term("foo").is_err());
433        assert!(isize::try_from_term("foo").is_err());
434        assert!(usize::try_from_term("foo").is_err());
435    }
436
437    #[test]
438    fn correct_datatype_to_native() {
439        assert_eq!(f64::try_from_term(3.15).unwrap(), 3.15);
440        assert_eq!(i32::try_from_term(42).unwrap(), 42);
441        assert_eq!(isize::try_from_term(42).unwrap(), 42);
442        assert_eq!(usize::try_from_term(42).unwrap(), 42);
443    }
444}