use crate::model::{BlankNode, Literal, NamedNode, Object, Predicate, Subject, Triple, Variable};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TriplePattern {
pub subject: Option<SubjectPattern>,
pub predicate: Option<PredicatePattern>,
pub object: Option<ObjectPattern>,
}
impl TriplePattern {
pub fn new(
subject: Option<SubjectPattern>,
predicate: Option<PredicatePattern>,
object: Option<ObjectPattern>,
) -> Self {
TriplePattern {
subject,
predicate,
object,
}
}
pub fn subject(&self) -> Option<&SubjectPattern> {
self.subject.as_ref()
}
pub fn predicate(&self) -> Option<&PredicatePattern> {
self.predicate.as_ref()
}
pub fn object(&self) -> Option<&ObjectPattern> {
self.object.as_ref()
}
pub fn matches(&self, triple: &Triple) -> bool {
if let Some(ref subject_pattern) = self.subject {
if !subject_pattern.matches(triple.subject()) {
return false;
}
}
if let Some(ref predicate_pattern) = self.predicate {
if !predicate_pattern.matches(triple.predicate()) {
return false;
}
}
if let Some(ref object_pattern) = self.object {
if !object_pattern.matches(triple.object()) {
return false;
}
}
true
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum SubjectPattern {
NamedNode(NamedNode),
BlankNode(BlankNode),
Variable(Variable),
QuotedTriple(Box<crate::query::algebra::AlgebraTriplePattern>),
}
impl SubjectPattern {
pub fn as_str(&self) -> &str {
match self {
SubjectPattern::NamedNode(nn) => nn.as_str(),
SubjectPattern::BlankNode(bn) => bn.as_str(),
SubjectPattern::Variable(v) => v.as_str(),
SubjectPattern::QuotedTriple(_) => "<<quoted-triple>>",
}
}
fn matches(&self, subject: &Subject) -> bool {
match (self, subject) {
(SubjectPattern::NamedNode(pn), Subject::NamedNode(sn)) => pn == sn,
(SubjectPattern::BlankNode(pb), Subject::BlankNode(sb)) => pb == sb,
(SubjectPattern::Variable(_), _) => true,
(SubjectPattern::QuotedTriple(_), Subject::QuotedTriple(_)) => true,
_ => false,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum PredicatePattern {
NamedNode(NamedNode),
Variable(Variable),
}
impl PredicatePattern {
pub fn as_str(&self) -> &str {
match self {
PredicatePattern::NamedNode(nn) => nn.as_str(),
PredicatePattern::Variable(v) => v.as_str(),
}
}
fn matches(&self, predicate: &Predicate) -> bool {
match (self, predicate) {
(PredicatePattern::NamedNode(pn), Predicate::NamedNode(sn)) => pn == sn,
(PredicatePattern::Variable(_), _) => true,
_ => false,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum ObjectPattern {
NamedNode(NamedNode),
BlankNode(BlankNode),
Literal(Literal),
Variable(Variable),
QuotedTriple(Box<crate::query::algebra::AlgebraTriplePattern>),
}
impl ObjectPattern {
pub fn as_str(&self) -> &str {
match self {
ObjectPattern::NamedNode(nn) => nn.as_str(),
ObjectPattern::BlankNode(bn) => bn.as_str(),
ObjectPattern::Literal(l) => l.value(),
ObjectPattern::Variable(v) => v.as_str(),
ObjectPattern::QuotedTriple(_) => "<<quoted-triple>>",
}
}
fn matches(&self, object: &Object) -> bool {
match (self, object) {
(ObjectPattern::NamedNode(pn), Object::NamedNode(on)) => pn == on,
(ObjectPattern::BlankNode(pb), Object::BlankNode(ob)) => pb == ob,
(ObjectPattern::Literal(pl), Object::Literal(ol)) => pl == ol,
(ObjectPattern::Variable(_), _) => true,
(ObjectPattern::QuotedTriple(_), Object::QuotedTriple(_)) => true,
_ => false,
}
}
}
use crate::query::algebra::TermPattern;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InvalidPatternConversion {
pub reason: &'static str,
}
impl std::fmt::Display for InvalidPatternConversion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Invalid pattern conversion: {}", self.reason)
}
}
impl std::error::Error for InvalidPatternConversion {}
impl TryFrom<TermPattern> for SubjectPattern {
type Error = InvalidPatternConversion;
fn try_from(term: TermPattern) -> Result<Self, Self::Error> {
match term {
TermPattern::NamedNode(n) => Ok(SubjectPattern::NamedNode(n)),
TermPattern::BlankNode(b) => Ok(SubjectPattern::BlankNode(b)),
TermPattern::Variable(v) => Ok(SubjectPattern::Variable(v)),
TermPattern::QuotedTriple(qt) => Ok(SubjectPattern::QuotedTriple(qt)),
TermPattern::Literal(_) => Err(InvalidPatternConversion {
reason: "Literals cannot be used as subjects in RDF",
}),
}
}
}
impl TryFrom<TermPattern> for PredicatePattern {
type Error = InvalidPatternConversion;
fn try_from(term: TermPattern) -> Result<Self, Self::Error> {
match term {
TermPattern::NamedNode(n) => Ok(PredicatePattern::NamedNode(n)),
TermPattern::Variable(v) => Ok(PredicatePattern::Variable(v)),
TermPattern::BlankNode(_) => Err(InvalidPatternConversion {
reason: "Blank nodes cannot be used as predicates in RDF",
}),
TermPattern::Literal(_) => Err(InvalidPatternConversion {
reason: "Literals cannot be used as predicates in RDF",
}),
TermPattern::QuotedTriple(_) => Err(InvalidPatternConversion {
reason: "Quoted triples cannot be used as predicates in RDF",
}),
}
}
}
impl TryFrom<TermPattern> for ObjectPattern {
type Error = InvalidPatternConversion;
fn try_from(term: TermPattern) -> Result<Self, Self::Error> {
match term {
TermPattern::NamedNode(n) => Ok(ObjectPattern::NamedNode(n)),
TermPattern::BlankNode(b) => Ok(ObjectPattern::BlankNode(b)),
TermPattern::Literal(l) => Ok(ObjectPattern::Literal(l)),
TermPattern::Variable(v) => Ok(ObjectPattern::Variable(v)),
TermPattern::QuotedTriple(qt) => Ok(ObjectPattern::QuotedTriple(qt)),
}
}
}
impl TryFrom<&SubjectPattern> for Subject {
type Error = ();
fn try_from(pattern: &SubjectPattern) -> Result<Self, Self::Error> {
match pattern {
SubjectPattern::NamedNode(n) => Ok(Subject::NamedNode(n.clone())),
SubjectPattern::BlankNode(b) => Ok(Subject::BlankNode(b.clone())),
SubjectPattern::Variable(_) => Err(()),
SubjectPattern::QuotedTriple(qt) => {
use crate::model::star::QuotedTriple;
let inner_subj = match &qt.subject {
TermPattern::NamedNode(n) => Subject::NamedNode(n.clone()),
TermPattern::BlankNode(b) => Subject::BlankNode(b.clone()),
_ => return Err(()),
};
let inner_pred = match &qt.predicate {
TermPattern::NamedNode(n) => Predicate::NamedNode(n.clone()),
_ => return Err(()),
};
let inner_obj = match &qt.object {
TermPattern::NamedNode(n) => Object::NamedNode(n.clone()),
TermPattern::BlankNode(b) => Object::BlankNode(b.clone()),
TermPattern::Literal(l) => Object::Literal(l.clone()),
_ => return Err(()),
};
Ok(Subject::QuotedTriple(Box::new(QuotedTriple::new(
Triple::new(inner_subj, inner_pred, inner_obj),
))))
}
}
}
}
impl TryFrom<&PredicatePattern> for Predicate {
type Error = ();
fn try_from(pattern: &PredicatePattern) -> Result<Self, Self::Error> {
match pattern {
PredicatePattern::NamedNode(n) => Ok(Predicate::NamedNode(n.clone())),
PredicatePattern::Variable(_) => Err(()),
}
}
}
impl TryFrom<&ObjectPattern> for Object {
type Error = ();
fn try_from(pattern: &ObjectPattern) -> Result<Self, Self::Error> {
match pattern {
ObjectPattern::NamedNode(n) => Ok(Object::NamedNode(n.clone())),
ObjectPattern::BlankNode(b) => Ok(Object::BlankNode(b.clone())),
ObjectPattern::Literal(l) => Ok(Object::Literal(l.clone())),
ObjectPattern::Variable(_) => Err(()),
ObjectPattern::QuotedTriple(qt) => {
use crate::model::star::QuotedTriple;
let inner_subj = match &qt.subject {
TermPattern::NamedNode(n) => Subject::NamedNode(n.clone()),
TermPattern::BlankNode(b) => Subject::BlankNode(b.clone()),
_ => return Err(()),
};
let inner_pred = match &qt.predicate {
TermPattern::NamedNode(n) => Predicate::NamedNode(n.clone()),
_ => return Err(()),
};
let inner_obj = match &qt.object {
TermPattern::NamedNode(n) => Object::NamedNode(n.clone()),
TermPattern::BlankNode(b) => Object::BlankNode(b.clone()),
TermPattern::Literal(l) => Object::Literal(l.clone()),
_ => return Err(()),
};
Ok(Object::QuotedTriple(Box::new(QuotedTriple::new(
Triple::new(inner_subj, inner_pred, inner_obj),
))))
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pattern_matching() {
let subject = NamedNode::new("http://example.org/s").expect("valid IRI");
let predicate = NamedNode::new("http://example.org/p").expect("valid IRI");
let object = Literal::new("o");
let triple = Triple::new(subject.clone(), predicate.clone(), object.clone());
let pattern = TriplePattern::new(
Some(SubjectPattern::NamedNode(subject.clone())),
Some(PredicatePattern::NamedNode(predicate.clone())),
Some(ObjectPattern::Literal(object.clone())),
);
assert!(pattern.matches(&triple));
let pattern = TriplePattern::new(None, None, None);
assert!(pattern.matches(&triple));
let pattern = TriplePattern::new(
Some(SubjectPattern::Variable(
Variable::new("s").expect("valid variable name"),
)),
Some(PredicatePattern::Variable(
Variable::new("p").expect("valid variable name"),
)),
Some(ObjectPattern::Variable(
Variable::new("o").expect("valid variable name"),
)),
);
assert!(pattern.matches(&triple));
let different_subject = NamedNode::new("http://example.org/different").expect("valid IRI");
let pattern = TriplePattern::new(
Some(SubjectPattern::NamedNode(different_subject)),
None,
None,
);
assert!(!pattern.matches(&triple));
}
}