diff --git a/api/src/term/_native_literal.rs b/api/src/term/_native_literal.rs index 46c66225..4410c178 100644 --- a/api/src/term/_native_literal.rs +++ b/api/src/term/_native_literal.rs @@ -5,6 +5,7 @@ lazy_static::lazy_static! { static ref XSD_DOUBLE: Box = xsd::double.iri().unwrap().unwrap().into(); static ref XSD_INTEGER: Box = xsd::integer.iri().unwrap().unwrap().into(); static ref XSD_STRING: Box = xsd::string.iri().unwrap().unwrap().into(); + static ref XSD_BOOLEAN: Box = xsd::boolean.iri().unwrap().unwrap().into(); } /// [`f64`] implements [`Term`] @@ -189,6 +190,42 @@ impl Term for str { } } +/// [`bool`] implements [`Term`] +/// so that Rust literals can be used as RDF literals in code. +/// +/// E.g.: +/// ``` +/// # use sophia_api::graph::{MutableGraph, Graph}; +/// # use sophia_api::term::SimpleTerm; +/// # use sophia_api::ns::{rdf, rdfs}; +/// # use sophia_iri::IriRef; +/// # fn test(graph: &mut T) -> Result<(), Box> { +/// # let subject: IriRef<&'static str> = IriRef::new("")?; +/// # +/// graph.insert(&subject, &rdf::value, true)?; +/// # +/// # Ok(()) } +/// ``` +impl Term for bool { + type BorrowTerm<'x> = Self; + + fn kind(&self) -> TermKind { + TermKind::Literal + } + fn lexical_form(&self) -> Option { + Some(MownStr::from(if *self { "true" } else { "false" })) + } + fn datatype(&self) -> Option> { + Some(IriRef::new_unchecked(MownStr::from_str(&XSD_BOOLEAN))) + } + fn language_tag(&self) -> Option> { + None + } + fn borrow_term(&self) -> Self::BorrowTerm<'_> { + *self + } +} + /// [`f64`] implements [`TryFromTerm`] /// so that compatible datatypes can easily be converted to native Rust values. impl TryFromTerm for f64 { @@ -226,6 +263,8 @@ impl TryFromTerm for i32 { || Term::eq(&term.datatype().unwrap(), xsd::unsignedByte) || Term::eq(&term.datatype().unwrap(), xsd::nonNegativeInteger) || Term::eq(&term.datatype().unwrap(), xsd::nonPositiveInteger) + || Term::eq(&term.datatype().unwrap(), xsd::negativeInteger) + || Term::eq(&term.datatype().unwrap(), xsd::positiveInteger) { lex.parse() } else { @@ -254,6 +293,8 @@ impl TryFromTerm for isize { || Term::eq(&term.datatype().unwrap(), xsd::unsignedByte) || Term::eq(&term.datatype().unwrap(), xsd::nonNegativeInteger) || Term::eq(&term.datatype().unwrap(), xsd::nonPositiveInteger) + || Term::eq(&term.datatype().unwrap(), xsd::negativeInteger) + || Term::eq(&term.datatype().unwrap(), xsd::positiveInteger) { lex.parse() } else { @@ -281,7 +322,7 @@ impl TryFromTerm for usize { || Term::eq(&term.datatype().unwrap(), xsd::unsignedShort) || Term::eq(&term.datatype().unwrap(), xsd::unsignedByte) || Term::eq(&term.datatype().unwrap(), xsd::nonNegativeInteger) - || Term::eq(&term.datatype().unwrap(), xsd::nonPositiveInteger) + || Term::eq(&term.datatype().unwrap(), xsd::positiveInteger) { lex.parse() } else { @@ -293,6 +334,24 @@ impl TryFromTerm for usize { } } +/// [`bool`] implements [`TryFromTerm`] +/// so that compatible datatypes can easily be converted to native Rust values. +impl TryFromTerm for bool { + type Error = std::str::ParseBoolError; + + fn try_from_term(term: T) -> Result { + if let Some(lex) = term.lexical_form() { + if Term::eq(&term.datatype().unwrap(), xsd::boolean) { + lex.parse() + } else { + "wrong datatype".parse() + } + } else { + "not a literal".parse() + } + } +} + #[cfg(test)] mod test { use super::*; @@ -348,6 +407,16 @@ mod test { assert_eq!(lit.borrow_term(), lit); } + #[test] + fn bool_as_literal() { + let lit = false; + assert_consistent_term_impl::(&lit); + assert_eq!(lit.kind(), TermKind::Literal); + assert_eq!(lit.lexical_form().unwrap(), "false"); + assert_eq!(lit.datatype(), xsd::boolean.iri()); + assert_eq!(lit.borrow_term(), lit); + } + #[test] fn iri_to_native() { assert!(f64::try_from_term(xsd::ID).is_err());