diff --git a/lib/oxrdf/src/lib.rs b/lib/oxrdf/src/lib.rs index b598cc03..34dcde82 100644 --- a/lib/oxrdf/src/lib.rs +++ b/lib/oxrdf/src/lib.rs @@ -34,6 +34,6 @@ pub use crate::triple::{ GraphName, GraphNameRef, NamedOrBlankNode, NamedOrBlankNodeRef, Quad, QuadRef, Subject, SubjectRef, Term, TermRef, Triple, TripleRef, }; -pub use crate::variable::{Variable, VariableNameParseError}; +pub use crate::variable::{Variable, VariableNameParseError, VariableRef}; pub use oxilangtag::LanguageTagParseError; pub use oxiri::IriParseError; diff --git a/lib/oxrdf/src/named_node.rs b/lib/oxrdf/src/named_node.rs index 976e26ed..56294415 100644 --- a/lib/oxrdf/src/named_node.rs +++ b/lib/oxrdf/src/named_node.rs @@ -1,4 +1,5 @@ use oxiri::{Iri, IriParseError}; +use std::cmp::Ordering; use std::fmt; /// An owned RDF [IRI](https://www.w3.org/TR/rdf11-concepts/#dfn-iri). @@ -154,6 +155,7 @@ impl From> for NamedNode { } impl<'a> From<&'a NamedNode> for NamedNodeRef<'a> { + #[inline] fn from(node: &'a NamedNode) -> Self { node.as_ref() } @@ -200,3 +202,17 @@ impl PartialEq> for &str { *self == other } } + +impl PartialOrd for NamedNodeRef<'_> { + #[inline] + fn partial_cmp(&self, other: &NamedNode) -> Option { + self.partial_cmp(&other.as_ref()) + } +} + +impl PartialOrd> for NamedNode { + #[inline] + fn partial_cmp(&self, other: &NamedNodeRef<'_>) -> Option { + self.as_ref().partial_cmp(other) + } +} diff --git a/lib/oxrdf/src/variable.rs b/lib/oxrdf/src/variable.rs index f46d363f..7645f963 100644 --- a/lib/oxrdf/src/variable.rs +++ b/lib/oxrdf/src/variable.rs @@ -1,8 +1,10 @@ +use std::cmp::Ordering; use std::error::Error; use std::fmt; -/// A [SPARQL query](https://www.w3.org/TR/sparql11-query/) variable. +/// A [SPARQL query](https://www.w3.org/TR/sparql11-query/) owned variable. /// +/// The default string formatter is returning a SPARQL compatible representation: /// ``` /// use oxrdf::{Variable, VariableNameParseError}; /// @@ -47,15 +49,124 @@ impl Variable { pub fn into_string(self) -> String { self.name } + + #[inline] + pub fn as_ref(&self) -> VariableRef<'_> { + VariableRef { name: &self.name } + } } impl fmt::Display for Variable { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.as_ref().fmt(f) + } +} + +/// A [SPARQL query](https://www.w3.org/TR/sparql11-query/) borrowed variable. +/// +/// The default string formatter is returning a SPARQL compatible representation: +/// ``` +/// use oxrdf::{VariableRef, VariableNameParseError}; +/// +/// assert_eq!( +/// "?foo", +/// VariableRef::new("foo")?.to_string() +/// ); +/// # Result::<_,VariableNameParseError>::Ok(()) +/// ``` +#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy, Hash)] +pub struct VariableRef<'a> { + name: &'a str, +} + +impl<'a> VariableRef<'a> { + /// Creates a variable name from a unique identifier. + /// + /// The variable identifier must be valid according to the SPARQL grammar. + pub fn new(name: &'a str) -> Result { + validate_variable_identifier(name)?; + Ok(Self::new_unchecked(name)) + } + + /// Creates a variable name from a unique identifier without validation. + /// + /// It is the caller's responsibility to ensure that `id` is a valid blank node identifier + /// according to the SPARQL grammar. + /// + /// [`Variable::new()`] is a safe version of this constructor and should be used for untrusted data. + #[inline] + pub fn new_unchecked(name: &'a str) -> Self { + Self { name } + } + + #[inline] + pub fn as_str(&self) -> &str { + self.name + } + + #[inline] + pub fn into_string(self) -> String { + self.name.to_owned() + } + + #[inline] + pub fn into_owned(self) -> Variable { + Variable { + name: self.name.to_owned(), + } + } +} + +impl fmt::Display for VariableRef<'_> { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "?{}", self.name) } } +impl<'a> From<&'a Variable> for VariableRef<'a> { + #[inline] + fn from(variable: &'a Variable) -> Self { + variable.as_ref() + } +} + +impl<'a> From> for Variable { + #[inline] + fn from(variable: VariableRef<'a>) -> Self { + variable.into_owned() + } +} + +impl PartialEq for VariableRef<'_> { + #[inline] + fn eq(&self, other: &Variable) -> bool { + *self == other.as_ref() + } +} + +impl PartialEq> for Variable { + #[inline] + fn eq(&self, other: &VariableRef<'_>) -> bool { + self.as_ref() == *other + } +} + +impl PartialOrd for VariableRef<'_> { + #[inline] + fn partial_cmp(&self, other: &Variable) -> Option { + self.partial_cmp(&other.as_ref()) + } +} + +impl PartialOrd> for Variable { + #[inline] + fn partial_cmp(&self, other: &VariableRef<'_>) -> Option { + self.as_ref().partial_cmp(other) + } +} + fn validate_variable_identifier(id: &str) -> Result<(), VariableNameParseError> { let mut chars = id.chars(); let front = chars.next().ok_or(VariableNameParseError {})?;