diff --git a/lib/oxrdf/src/blank_node.rs b/lib/oxrdf/src/blank_node.rs index 6d565fda..f39574a2 100644 --- a/lib/oxrdf/src/blank_node.rs +++ b/lib/oxrdf/src/blank_node.rs @@ -2,6 +2,8 @@ use rand::random; use std::io::Write; use std::{fmt, str}; +use crate::Term; + /// An owned RDF [blank node](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node). /// /// The common way to create a new blank node is to use the [`BlankNode::default()`] function. @@ -102,6 +104,16 @@ impl fmt::Display for BlankNode { } } +impl From for Option { + #[inline] + fn from(term: Term) -> Self { + match term { + Term::BlankNode(node) => Some(node), + _ => None, + } + } +} + impl Default for BlankNode { /// Builds a new RDF [blank node](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node) with a unique id. #[inline] @@ -352,6 +364,8 @@ pub struct BlankNodeIdParseError; mod tests { #![allow(clippy::panic_in_result_fn)] + use crate::{Literal, NamedNode}; + use super::*; #[test] @@ -360,6 +374,18 @@ mod tests { assert_eq!(b.as_str(), "42"); } + #[test] + fn casting() { + let bnode: Option = Term::BlankNode(BlankNode::new_from_unique_id(0x42)).into(); + assert_eq!(bnode, Some(BlankNode::new_from_unique_id(0x42))); + + let literal: Option = Term::Literal(Literal::new_simple_literal("Hello World!")).into(); + assert_eq!(literal, None); + + let named_node: Option = Term::NamedNode(NamedNode::new("http://example.org/test").unwrap()).into(); + assert_eq!(named_node, None); + } + #[test] fn as_str_full() { let b = BlankNode::new_from_unique_id(0x7777_6666_5555_4444_3333_2222_1111_0000); diff --git a/lib/oxrdf/src/literal.rs b/lib/oxrdf/src/literal.rs index 13ab07fe..78d4e3e1 100644 --- a/lib/oxrdf/src/literal.rs +++ b/lib/oxrdf/src/literal.rs @@ -1,6 +1,6 @@ use crate::named_node::NamedNode; use crate::vocab::{rdf, xsd}; -use crate::NamedNodeRef; +use crate::{NamedNodeRef, Term}; use oxilangtag::{LanguageTag, LanguageTagParseError}; #[cfg(feature = "oxsdatatypes")] use oxsdatatypes::*; @@ -161,6 +161,16 @@ impl fmt::Display for Literal { } } +impl From for Option { + #[inline] + fn from(term: Term) -> Self { + match term { + Term::Literal(literal) => Some(literal), + _ => None, + } + } +} + impl<'a> From<&'a str> for Literal { #[inline] fn from(value: &'a str) -> Self { @@ -637,6 +647,8 @@ pub fn print_quoted_str(string: &str, f: &mut impl Write) -> fmt::Result { mod tests { #![allow(clippy::panic_in_result_fn)] + use crate::BlankNode; + use super::*; #[test] @@ -659,6 +671,19 @@ mod tests { ); } + + #[test] + fn casting() { + let literal: Option = Term::Literal(Literal::new_simple_literal("Hello World!")).into(); + assert_eq!(literal, Some(Literal::new_simple_literal("Hello World!"))); + + let bnode: Option = Term::BlankNode(BlankNode::new_from_unique_id(0x42)).into(); + assert_eq!(bnode, None); + + let named_node: Option = Term::NamedNode(NamedNode::new("http://example.org/test").unwrap()).into(); + assert_eq!(named_node, None); + } + #[test] fn test_float_format() { assert_eq!("INF", Literal::from(f32::INFINITY).value()); diff --git a/lib/oxrdf/src/named_node.rs b/lib/oxrdf/src/named_node.rs index 9b545bcc..95c299bf 100644 --- a/lib/oxrdf/src/named_node.rs +++ b/lib/oxrdf/src/named_node.rs @@ -2,6 +2,8 @@ use oxiri::{Iri, IriParseError}; use std::cmp::Ordering; use std::fmt; +use crate::Term; + /// An owned RDF [IRI](https://www.w3.org/TR/rdf11-concepts/#dfn-iri). /// /// The default string formatter is returning an N-Triples, Turtle, and SPARQL compatible representation: @@ -217,6 +219,16 @@ impl PartialOrd> for NamedNode { } } +impl From for Option { + #[inline] + fn from(term: Term) -> Self { + match term { + Term::NamedNode(node) => Some(node), + _ => None, + } + } +} + impl From> for NamedNode { #[inline] fn from(iri: Iri) -> Self { @@ -234,3 +246,24 @@ impl<'a> From> for NamedNodeRef<'a> { } } } + +#[cfg(test)] +mod tests { + #![allow(clippy::panic_in_result_fn)] + + use crate::{Literal, BlankNode}; + + use super::*; + + #[test] + fn casting() { + let named_node: Option = Term::NamedNode(NamedNode::new("http://example.org/test").unwrap()).into(); + assert_eq!(named_node, Some(NamedNode::new("http://example.org/test").unwrap())); + + let literal: Option = Term::Literal(Literal::new_simple_literal("Hello World!")).into(); + assert_eq!(literal, None); + + let bnode: Option = Term::BlankNode(BlankNode::new_from_unique_id(0x42)).into(); + assert_eq!(bnode, None); + } +} diff --git a/lib/oxrdf/src/triple.rs b/lib/oxrdf/src/triple.rs index 76039644..ca594836 100644 --- a/lib/oxrdf/src/triple.rs +++ b/lib/oxrdf/src/triple.rs @@ -195,6 +195,19 @@ impl fmt::Display for Subject { } } +impl From for Option { + #[inline] + fn from(term: Term) -> Self { + match term { + Term::NamedNode(node) => Some(Subject::NamedNode(node)), + Term::BlankNode(node) => Some(Subject::BlankNode(node)), + #[cfg(feature = "rdf-star")] + Term::Triple(triple) => Some(Subject::Triple(triple)), + Term::Literal(_) => None + } + } +} + impl From for Subject { #[inline] fn from(node: NamedNode) -> Self { @@ -738,6 +751,23 @@ impl Triple { } } + /// Builds an RDF [triple](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-triple). + #[inline] + pub fn new_maybe( + subject: impl Into>, + predicate: impl Into>, + object: impl Into>, + ) -> Option { + match (subject.into(), predicate.into(), object.into()) { + (Some(subject), Some(predicate), Some(object)) => Some(Self { + subject, + predicate, + object, + }), + _ => None, + } + } + /// Encodes that this triple is in an [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset). #[inline] pub fn in_graph(self, graph_name: impl Into) -> Quad { @@ -766,6 +796,18 @@ impl fmt::Display for Triple { } } +#[cfg(feature = "rdf-star")] +impl From for Option> { + #[inline] + fn from(term: Term) -> Self { + match term { + #[cfg(feature = "rdf-star")] + Term::Triple(triple) => Some(triple), + _ => None, + } + } +} + /// A borrowed [RDF triple](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-triple). /// /// The default string formatter is returning an N-Triples, Turtle, and SPARQL compatible representation: @@ -1224,3 +1266,83 @@ impl<'a> From> for Quad { quad.into_owned() } } + +#[cfg(test)] +mod tests { + #![allow(clippy::panic_in_result_fn)] + + use crate::{Literal, BlankNode}; + + use super::*; + + #[test] + #[cfg(feature = "rdf-star")] + fn casting_triple() { + let triple = Triple { + subject: NamedNode::new("http://example.org/s").unwrap().into(), + predicate: NamedNode::new("http://example.org/p").unwrap(), + object: NamedNode::new("http://example.org/o").unwrap().into(), + }; + let triple_box = Box::new(triple); + + let t: Option> = Term::Triple(triple_box.clone()).into(); + assert_eq!(t, Some(triple_box.clone())); + + let literal: Option> = Term::Literal(Literal::new_simple_literal("Hello World!")).into(); + assert_eq!(literal, None); + + let bnode: Option> = Term::BlankNode(BlankNode::new_from_unique_id(0x42)).into(); + assert_eq!(bnode, None); + + let named_node: Option> = Term::NamedNode(NamedNode::new("http://example.org/test").unwrap()).into(); + assert_eq!(named_node, None); + } + + #[test] + #[cfg(feature = "rdf-star")] + fn constructing_triple() { + let optional_triple = Triple::new_maybe( + Term::NamedNode(NamedNode::new("http://example.org/test").unwrap()), + Term::NamedNode(NamedNode::new("http://example.org/test").unwrap()), + Term::NamedNode(NamedNode::new("http://example.org/test").unwrap()) + ); + + let bad_triple = Triple::new_maybe( + Term::BlankNode(BlankNode::new("abc123").unwrap()), + Term::NamedNode(NamedNode::new("http://example.org/test").unwrap()), + Term::NamedNode(NamedNode::new("http://example.org/test").unwrap()) + ); + + let triple: Triple = Triple::new( + Subject::NamedNode(NamedNode::new("http://example.org/test").unwrap()), + NamedNode::new("http://example.org/test").unwrap(), + Term::NamedNode(NamedNode::new("http://example.org/test").unwrap()) + ); + + assert_eq!(optional_triple, Some(triple)); + assert_eq!(bad_triple, None); + } + + #[test] + #[cfg(feature = "rdf-star")] + fn casting_subject() { + let triple = Triple { + subject: NamedNode::new("http://example.org/s").unwrap().into(), + predicate: NamedNode::new("http://example.org/p").unwrap(), + object: NamedNode::new("http://example.org/o").unwrap().into(), + }; + let triple_box = Box::new(triple); + + let t: Option = Term::Triple(triple_box.clone()).into(); + assert_eq!(t, Some(Subject::Triple(triple_box.clone()))); + + let literal: Option = Term::Literal(Literal::new_simple_literal("Hello World!")).into(); + assert_eq!(literal, None); + + let bnode: Option = Term::BlankNode(BlankNode::new_from_unique_id(0x42)).into(); + assert_eq!(bnode, Some(Subject::BlankNode(BlankNode::new_from_unique_id(0x42)))); + + let named_node: Option = Term::NamedNode(NamedNode::new("http://example.org/test").unwrap()).into(); + assert_eq!(named_node, Some(Subject::NamedNode(NamedNode::new("http://example.org/test").unwrap()))); + } +}