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