1use crate::model::{BlankNode, Literal, NamedNode, Object, Predicate, Subject, Triple, Variable};
6
7#[derive(Debug, Clone, PartialEq, Eq, Hash)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10pub struct TriplePattern {
11 pub subject: Option<SubjectPattern>,
12 pub predicate: Option<PredicatePattern>,
13 pub object: Option<ObjectPattern>,
14}
15
16impl TriplePattern {
17 pub fn new(
19 subject: Option<SubjectPattern>,
20 predicate: Option<PredicatePattern>,
21 object: Option<ObjectPattern>,
22 ) -> Self {
23 TriplePattern {
24 subject,
25 predicate,
26 object,
27 }
28 }
29
30 pub fn subject(&self) -> Option<&SubjectPattern> {
32 self.subject.as_ref()
33 }
34
35 pub fn predicate(&self) -> Option<&PredicatePattern> {
37 self.predicate.as_ref()
38 }
39
40 pub fn object(&self) -> Option<&ObjectPattern> {
42 self.object.as_ref()
43 }
44
45 pub fn matches(&self, triple: &Triple) -> bool {
47 if let Some(ref subject_pattern) = self.subject {
49 if !subject_pattern.matches(triple.subject()) {
50 return false;
51 }
52 }
53
54 if let Some(ref predicate_pattern) = self.predicate {
56 if !predicate_pattern.matches(triple.predicate()) {
57 return false;
58 }
59 }
60
61 if let Some(ref object_pattern) = self.object {
63 if !object_pattern.matches(triple.object()) {
64 return false;
65 }
66 }
67
68 true
69 }
70}
71
72#[derive(Debug, Clone, PartialEq, Eq, Hash)]
74#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
75pub enum SubjectPattern {
76 NamedNode(NamedNode),
77 BlankNode(BlankNode),
78 Variable(Variable),
79}
80
81impl SubjectPattern {
82 pub fn as_str(&self) -> &str {
84 match self {
85 SubjectPattern::NamedNode(nn) => nn.as_str(),
86 SubjectPattern::BlankNode(bn) => bn.as_str(),
87 SubjectPattern::Variable(v) => v.as_str(),
88 }
89 }
90
91 fn matches(&self, subject: &Subject) -> bool {
92 match (self, subject) {
93 (SubjectPattern::NamedNode(pn), Subject::NamedNode(sn)) => pn == sn,
94 (SubjectPattern::BlankNode(pb), Subject::BlankNode(sb)) => pb == sb,
95 (SubjectPattern::Variable(_), _) => true,
96 _ => false,
97 }
98 }
99}
100
101#[derive(Debug, Clone, PartialEq, Eq, Hash)]
103#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
104pub enum PredicatePattern {
105 NamedNode(NamedNode),
106 Variable(Variable),
107}
108
109impl PredicatePattern {
110 pub fn as_str(&self) -> &str {
112 match self {
113 PredicatePattern::NamedNode(nn) => nn.as_str(),
114 PredicatePattern::Variable(v) => v.as_str(),
115 }
116 }
117
118 fn matches(&self, predicate: &Predicate) -> bool {
119 match (self, predicate) {
120 (PredicatePattern::NamedNode(pn), Predicate::NamedNode(sn)) => pn == sn,
121 (PredicatePattern::Variable(_), _) => true,
122 _ => false,
123 }
124 }
125}
126
127#[derive(Debug, Clone, PartialEq, Eq, Hash)]
129#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
130pub enum ObjectPattern {
131 NamedNode(NamedNode),
132 BlankNode(BlankNode),
133 Literal(Literal),
134 Variable(Variable),
135}
136
137impl ObjectPattern {
138 pub fn as_str(&self) -> &str {
140 match self {
141 ObjectPattern::NamedNode(nn) => nn.as_str(),
142 ObjectPattern::BlankNode(bn) => bn.as_str(),
143 ObjectPattern::Literal(l) => l.value(),
144 ObjectPattern::Variable(v) => v.as_str(),
145 }
146 }
147
148 fn matches(&self, object: &Object) -> bool {
149 match (self, object) {
150 (ObjectPattern::NamedNode(pn), Object::NamedNode(on)) => pn == on,
151 (ObjectPattern::BlankNode(pb), Object::BlankNode(ob)) => pb == ob,
152 (ObjectPattern::Literal(pl), Object::Literal(ol)) => pl == ol,
153 (ObjectPattern::Variable(_), _) => true,
154 _ => false,
155 }
156 }
157}
158
159use crate::query::algebra::TermPattern;
161
162impl From<TermPattern> for SubjectPattern {
163 fn from(term: TermPattern) -> Self {
164 match term {
165 TermPattern::NamedNode(n) => SubjectPattern::NamedNode(n),
166 TermPattern::BlankNode(b) => SubjectPattern::BlankNode(b),
167 TermPattern::Variable(v) => SubjectPattern::Variable(v),
168 TermPattern::Literal(_) => panic!("Literals cannot be subjects"),
169 TermPattern::QuotedTriple(_) => {
170 panic!("RDF-star quoted triples as subjects not yet fully implemented")
171 }
172 }
173 }
174}
175
176impl From<TermPattern> for PredicatePattern {
177 fn from(term: TermPattern) -> Self {
178 match term {
179 TermPattern::NamedNode(n) => PredicatePattern::NamedNode(n),
180 TermPattern::Variable(v) => PredicatePattern::Variable(v),
181 TermPattern::BlankNode(_) => panic!("Blank nodes cannot be predicates"),
182 TermPattern::Literal(_) => panic!("Literals cannot be predicates"),
183 TermPattern::QuotedTriple(_) => panic!("Quoted triples cannot be predicates"),
184 }
185 }
186}
187
188impl From<TermPattern> for ObjectPattern {
189 fn from(term: TermPattern) -> Self {
190 match term {
191 TermPattern::NamedNode(n) => ObjectPattern::NamedNode(n),
192 TermPattern::BlankNode(b) => ObjectPattern::BlankNode(b),
193 TermPattern::Literal(l) => ObjectPattern::Literal(l),
194 TermPattern::Variable(v) => ObjectPattern::Variable(v),
195 TermPattern::QuotedTriple(_) => {
196 panic!("RDF-star quoted triples as objects not yet fully implemented")
197 }
198 }
199 }
200}
201
202impl TryFrom<&SubjectPattern> for Subject {
204 type Error = ();
205
206 fn try_from(pattern: &SubjectPattern) -> Result<Self, Self::Error> {
207 match pattern {
208 SubjectPattern::NamedNode(n) => Ok(Subject::NamedNode(n.clone())),
209 SubjectPattern::BlankNode(b) => Ok(Subject::BlankNode(b.clone())),
210 SubjectPattern::Variable(_) => Err(()),
211 }
212 }
213}
214
215impl TryFrom<&PredicatePattern> for Predicate {
216 type Error = ();
217
218 fn try_from(pattern: &PredicatePattern) -> Result<Self, Self::Error> {
219 match pattern {
220 PredicatePattern::NamedNode(n) => Ok(Predicate::NamedNode(n.clone())),
221 PredicatePattern::Variable(_) => Err(()),
222 }
223 }
224}
225
226impl TryFrom<&ObjectPattern> for Object {
227 type Error = ();
228
229 fn try_from(pattern: &ObjectPattern) -> Result<Self, Self::Error> {
230 match pattern {
231 ObjectPattern::NamedNode(n) => Ok(Object::NamedNode(n.clone())),
232 ObjectPattern::BlankNode(b) => Ok(Object::BlankNode(b.clone())),
233 ObjectPattern::Literal(l) => Ok(Object::Literal(l.clone())),
234 ObjectPattern::Variable(_) => Err(()),
235 }
236 }
237}
238
239#[cfg(test)]
240mod tests {
241 use super::*;
242
243 #[test]
244 fn test_pattern_matching() {
245 let subject = NamedNode::new("http://example.org/s").unwrap();
246 let predicate = NamedNode::new("http://example.org/p").unwrap();
247 let object = Literal::new("o");
248
249 let triple = Triple::new(subject.clone(), predicate.clone(), object.clone());
250
251 let pattern = TriplePattern::new(
253 Some(SubjectPattern::NamedNode(subject.clone())),
254 Some(PredicatePattern::NamedNode(predicate.clone())),
255 Some(ObjectPattern::Literal(object.clone())),
256 );
257 assert!(pattern.matches(&triple));
258
259 let pattern = TriplePattern::new(None, None, None);
261 assert!(pattern.matches(&triple));
262
263 let pattern = TriplePattern::new(
265 Some(SubjectPattern::Variable(Variable::new("s").unwrap())),
266 Some(PredicatePattern::Variable(Variable::new("p").unwrap())),
267 Some(ObjectPattern::Variable(Variable::new("o").unwrap())),
268 );
269 assert!(pattern.matches(&triple));
270
271 let different_subject = NamedNode::new("http://example.org/different").unwrap();
273 let pattern = TriplePattern::new(
274 Some(SubjectPattern::NamedNode(different_subject)),
275 None,
276 None,
277 );
278 assert!(!pattern.matches(&triple));
279 }
280}