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),
}
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(),
}
}
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,
_ => 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),
}
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(),
}
}
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,
_ => false,
}
}
}
use crate::query::algebra::TermPattern;
impl From<TermPattern> for SubjectPattern {
fn from(term: TermPattern) -> Self {
match term {
TermPattern::NamedNode(n) => SubjectPattern::NamedNode(n),
TermPattern::BlankNode(b) => SubjectPattern::BlankNode(b),
TermPattern::Variable(v) => SubjectPattern::Variable(v),
TermPattern::Literal(_) => panic!("Literals cannot be subjects"),
TermPattern::QuotedTriple(_) => {
panic!("RDF-star quoted triples as subjects not yet fully implemented")
}
}
}
}
impl From<TermPattern> for PredicatePattern {
fn from(term: TermPattern) -> Self {
match term {
TermPattern::NamedNode(n) => PredicatePattern::NamedNode(n),
TermPattern::Variable(v) => PredicatePattern::Variable(v),
TermPattern::BlankNode(_) => panic!("Blank nodes cannot be predicates"),
TermPattern::Literal(_) => panic!("Literals cannot be predicates"),
TermPattern::QuotedTriple(_) => panic!("Quoted triples cannot be predicates"),
}
}
}
impl From<TermPattern> for ObjectPattern {
fn from(term: TermPattern) -> Self {
match term {
TermPattern::NamedNode(n) => ObjectPattern::NamedNode(n),
TermPattern::BlankNode(b) => ObjectPattern::BlankNode(b),
TermPattern::Literal(l) => ObjectPattern::Literal(l),
TermPattern::Variable(v) => ObjectPattern::Variable(v),
TermPattern::QuotedTriple(_) => {
panic!("RDF-star quoted triples as objects not yet fully implemented")
}
}
}
}
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(()),
}
}
}
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(()),
}
}
}
#[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));
}
}