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 QuotedTriple(Box<crate::query::algebra::AlgebraTriplePattern>),
81}
82
83impl SubjectPattern {
84 pub fn as_str(&self) -> &str {
86 match self {
87 SubjectPattern::NamedNode(nn) => nn.as_str(),
88 SubjectPattern::BlankNode(bn) => bn.as_str(),
89 SubjectPattern::Variable(v) => v.as_str(),
90 SubjectPattern::QuotedTriple(_) => "<<quoted-triple>>",
91 }
92 }
93
94 fn matches(&self, subject: &Subject) -> bool {
95 match (self, subject) {
96 (SubjectPattern::NamedNode(pn), Subject::NamedNode(sn)) => pn == sn,
97 (SubjectPattern::BlankNode(pb), Subject::BlankNode(sb)) => pb == sb,
98 (SubjectPattern::Variable(_), _) => true,
99 (SubjectPattern::QuotedTriple(pat), Subject::QuotedTriple(sqt)) => {
103 algebra_pattern_matches_triple(pat, sqt.inner())
104 }
105 _ => false,
106 }
107 }
108}
109
110pub(crate) fn algebra_pattern_matches_triple(
118 pattern: &crate::query::algebra::AlgebraTriplePattern,
119 triple: &Triple,
120) -> bool {
121 term_pattern_matches_subject(&pattern.subject, triple.subject())
122 && term_pattern_matches_predicate(&pattern.predicate, triple.predicate())
123 && term_pattern_matches_object(&pattern.object, triple.object())
124}
125
126fn term_pattern_matches_subject(
127 pattern: &crate::query::algebra::TermPattern,
128 subject: &Subject,
129) -> bool {
130 use crate::query::algebra::TermPattern;
131 match (pattern, subject) {
132 (TermPattern::Variable(_), _) => true,
133 (TermPattern::NamedNode(pn), Subject::NamedNode(sn)) => pn == sn,
134 (TermPattern::BlankNode(pb), Subject::BlankNode(sb)) => pb == sb,
135 (TermPattern::QuotedTriple(pat), Subject::QuotedTriple(sqt)) => {
136 algebra_pattern_matches_triple(pat, sqt.inner())
137 }
138 _ => false,
139 }
140}
141
142fn term_pattern_matches_predicate(
143 pattern: &crate::query::algebra::TermPattern,
144 predicate: &Predicate,
145) -> bool {
146 use crate::query::algebra::TermPattern;
147 match (pattern, predicate) {
148 (TermPattern::Variable(_), _) => true,
149 (TermPattern::NamedNode(pn), Predicate::NamedNode(sn)) => pn == sn,
150 _ => false,
151 }
152}
153
154fn term_pattern_matches_object(
155 pattern: &crate::query::algebra::TermPattern,
156 object: &Object,
157) -> bool {
158 use crate::query::algebra::TermPattern;
159 match (pattern, object) {
160 (TermPattern::Variable(_), _) => true,
161 (TermPattern::NamedNode(pn), Object::NamedNode(on)) => pn == on,
162 (TermPattern::BlankNode(pb), Object::BlankNode(ob)) => pb == ob,
163 (TermPattern::Literal(pl), Object::Literal(ol)) => pl == ol,
164 (TermPattern::QuotedTriple(pat), Object::QuotedTriple(oqt)) => {
165 algebra_pattern_matches_triple(pat, oqt.inner())
166 }
167 _ => false,
168 }
169}
170
171#[derive(Debug, Clone, PartialEq, Eq, Hash)]
173#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
174pub enum PredicatePattern {
175 NamedNode(NamedNode),
176 Variable(Variable),
177}
178
179impl PredicatePattern {
180 pub fn as_str(&self) -> &str {
182 match self {
183 PredicatePattern::NamedNode(nn) => nn.as_str(),
184 PredicatePattern::Variable(v) => v.as_str(),
185 }
186 }
187
188 fn matches(&self, predicate: &Predicate) -> bool {
189 match (self, predicate) {
190 (PredicatePattern::NamedNode(pn), Predicate::NamedNode(sn)) => pn == sn,
191 (PredicatePattern::Variable(_), _) => true,
192 _ => false,
193 }
194 }
195}
196
197#[derive(Debug, Clone, PartialEq, Eq, Hash)]
199#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
200pub enum ObjectPattern {
201 NamedNode(NamedNode),
202 BlankNode(BlankNode),
203 Literal(Literal),
204 Variable(Variable),
205 QuotedTriple(Box<crate::query::algebra::AlgebraTriplePattern>),
207}
208
209impl ObjectPattern {
210 pub fn as_str(&self) -> &str {
212 match self {
213 ObjectPattern::NamedNode(nn) => nn.as_str(),
214 ObjectPattern::BlankNode(bn) => bn.as_str(),
215 ObjectPattern::Literal(l) => l.value(),
216 ObjectPattern::Variable(v) => v.as_str(),
217 ObjectPattern::QuotedTriple(_) => "<<quoted-triple>>",
218 }
219 }
220
221 fn matches(&self, object: &Object) -> bool {
222 match (self, object) {
223 (ObjectPattern::NamedNode(pn), Object::NamedNode(on)) => pn == on,
224 (ObjectPattern::BlankNode(pb), Object::BlankNode(ob)) => pb == ob,
225 (ObjectPattern::Literal(pl), Object::Literal(ol)) => pl == ol,
226 (ObjectPattern::Variable(_), _) => true,
227 (ObjectPattern::QuotedTriple(pat), Object::QuotedTriple(oqt)) => {
231 algebra_pattern_matches_triple(pat, oqt.inner())
232 }
233 _ => false,
234 }
235 }
236}
237
238use crate::query::algebra::TermPattern;
242
243#[derive(Debug, Clone, PartialEq, Eq)]
245pub struct InvalidPatternConversion {
246 pub reason: &'static str,
247}
248
249impl std::fmt::Display for InvalidPatternConversion {
250 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
251 write!(f, "Invalid pattern conversion: {}", self.reason)
252 }
253}
254
255impl std::error::Error for InvalidPatternConversion {}
256
257impl TryFrom<TermPattern> for SubjectPattern {
258 type Error = InvalidPatternConversion;
259
260 fn try_from(term: TermPattern) -> Result<Self, Self::Error> {
261 match term {
262 TermPattern::NamedNode(n) => Ok(SubjectPattern::NamedNode(n)),
263 TermPattern::BlankNode(b) => Ok(SubjectPattern::BlankNode(b)),
264 TermPattern::Variable(v) => Ok(SubjectPattern::Variable(v)),
265 TermPattern::QuotedTriple(qt) => Ok(SubjectPattern::QuotedTriple(qt)),
266 TermPattern::Literal(_) => Err(InvalidPatternConversion {
267 reason: "Literals cannot be used as subjects in RDF",
268 }),
269 }
270 }
271}
272
273impl TryFrom<TermPattern> for PredicatePattern {
274 type Error = InvalidPatternConversion;
275
276 fn try_from(term: TermPattern) -> Result<Self, Self::Error> {
277 match term {
278 TermPattern::NamedNode(n) => Ok(PredicatePattern::NamedNode(n)),
279 TermPattern::Variable(v) => Ok(PredicatePattern::Variable(v)),
280 TermPattern::BlankNode(_) => Err(InvalidPatternConversion {
281 reason: "Blank nodes cannot be used as predicates in RDF",
282 }),
283 TermPattern::Literal(_) => Err(InvalidPatternConversion {
284 reason: "Literals cannot be used as predicates in RDF",
285 }),
286 TermPattern::QuotedTriple(_) => Err(InvalidPatternConversion {
287 reason: "Quoted triples cannot be used as predicates in RDF",
288 }),
289 }
290 }
291}
292
293impl TryFrom<TermPattern> for ObjectPattern {
294 type Error = InvalidPatternConversion;
295
296 fn try_from(term: TermPattern) -> Result<Self, Self::Error> {
297 match term {
298 TermPattern::NamedNode(n) => Ok(ObjectPattern::NamedNode(n)),
299 TermPattern::BlankNode(b) => Ok(ObjectPattern::BlankNode(b)),
300 TermPattern::Literal(l) => Ok(ObjectPattern::Literal(l)),
301 TermPattern::Variable(v) => Ok(ObjectPattern::Variable(v)),
302 TermPattern::QuotedTriple(qt) => Ok(ObjectPattern::QuotedTriple(qt)),
303 }
304 }
305}
306
307impl TryFrom<&SubjectPattern> for Subject {
309 type Error = ();
310
311 fn try_from(pattern: &SubjectPattern) -> Result<Self, Self::Error> {
312 match pattern {
313 SubjectPattern::NamedNode(n) => Ok(Subject::NamedNode(n.clone())),
314 SubjectPattern::BlankNode(b) => Ok(Subject::BlankNode(b.clone())),
315 SubjectPattern::Variable(_) => Err(()),
316 SubjectPattern::QuotedTriple(qt) => {
317 use crate::model::star::QuotedTriple;
320 let inner_subj = match &qt.subject {
321 TermPattern::NamedNode(n) => Subject::NamedNode(n.clone()),
322 TermPattern::BlankNode(b) => Subject::BlankNode(b.clone()),
323 _ => return Err(()),
324 };
325 let inner_pred = match &qt.predicate {
326 TermPattern::NamedNode(n) => Predicate::NamedNode(n.clone()),
327 _ => return Err(()),
328 };
329 let inner_obj = match &qt.object {
330 TermPattern::NamedNode(n) => Object::NamedNode(n.clone()),
331 TermPattern::BlankNode(b) => Object::BlankNode(b.clone()),
332 TermPattern::Literal(l) => Object::Literal(l.clone()),
333 _ => return Err(()),
334 };
335 Ok(Subject::QuotedTriple(Box::new(QuotedTriple::new(
336 Triple::new(inner_subj, inner_pred, inner_obj),
337 ))))
338 }
339 }
340 }
341}
342
343impl TryFrom<&PredicatePattern> for Predicate {
344 type Error = ();
345
346 fn try_from(pattern: &PredicatePattern) -> Result<Self, Self::Error> {
347 match pattern {
348 PredicatePattern::NamedNode(n) => Ok(Predicate::NamedNode(n.clone())),
349 PredicatePattern::Variable(_) => Err(()),
350 }
351 }
352}
353
354impl TryFrom<&ObjectPattern> for Object {
355 type Error = ();
356
357 fn try_from(pattern: &ObjectPattern) -> Result<Self, Self::Error> {
358 match pattern {
359 ObjectPattern::NamedNode(n) => Ok(Object::NamedNode(n.clone())),
360 ObjectPattern::BlankNode(b) => Ok(Object::BlankNode(b.clone())),
361 ObjectPattern::Literal(l) => Ok(Object::Literal(l.clone())),
362 ObjectPattern::Variable(_) => Err(()),
363 ObjectPattern::QuotedTriple(qt) => {
364 use crate::model::star::QuotedTriple;
366 let inner_subj = match &qt.subject {
367 TermPattern::NamedNode(n) => Subject::NamedNode(n.clone()),
368 TermPattern::BlankNode(b) => Subject::BlankNode(b.clone()),
369 _ => return Err(()),
370 };
371 let inner_pred = match &qt.predicate {
372 TermPattern::NamedNode(n) => Predicate::NamedNode(n.clone()),
373 _ => return Err(()),
374 };
375 let inner_obj = match &qt.object {
376 TermPattern::NamedNode(n) => Object::NamedNode(n.clone()),
377 TermPattern::BlankNode(b) => Object::BlankNode(b.clone()),
378 TermPattern::Literal(l) => Object::Literal(l.clone()),
379 _ => return Err(()),
380 };
381 Ok(Object::QuotedTriple(Box::new(QuotedTriple::new(
382 Triple::new(inner_subj, inner_pred, inner_obj),
383 ))))
384 }
385 }
386 }
387}
388
389#[cfg(test)]
390mod tests {
391 use super::*;
392
393 #[test]
394 fn test_pattern_matching() {
395 let subject = NamedNode::new("http://example.org/s").expect("valid IRI");
396 let predicate = NamedNode::new("http://example.org/p").expect("valid IRI");
397 let object = Literal::new("o");
398
399 let triple = Triple::new(subject.clone(), predicate.clone(), object.clone());
400
401 let pattern = TriplePattern::new(
403 Some(SubjectPattern::NamedNode(subject.clone())),
404 Some(PredicatePattern::NamedNode(predicate.clone())),
405 Some(ObjectPattern::Literal(object.clone())),
406 );
407 assert!(pattern.matches(&triple));
408
409 let pattern = TriplePattern::new(None, None, None);
411 assert!(pattern.matches(&triple));
412
413 let pattern = TriplePattern::new(
415 Some(SubjectPattern::Variable(
416 Variable::new("s").expect("valid variable name"),
417 )),
418 Some(PredicatePattern::Variable(
419 Variable::new("p").expect("valid variable name"),
420 )),
421 Some(ObjectPattern::Variable(
422 Variable::new("o").expect("valid variable name"),
423 )),
424 );
425 assert!(pattern.matches(&triple));
426
427 let different_subject = NamedNode::new("http://example.org/different").expect("valid IRI");
429 let pattern = TriplePattern::new(
430 Some(SubjectPattern::NamedNode(different_subject)),
431 None,
432 None,
433 );
434 assert!(!pattern.matches(&triple));
435 }
436
437 #[test]
438 fn regression_ground_quoted_triple_pattern_is_exact() {
439 use crate::model::star::QuotedTriple;
440 use crate::query::algebra::{AlgebraTriplePattern, TermPattern};
441
442 let nn = |s: &str| NamedNode::new(s).expect("valid IRI");
443
444 let stored_inner = Triple::new(nn("http://ex/a"), nn("http://ex/b"), nn("http://ex/c"));
446 let subject = Subject::QuotedTriple(Box::new(QuotedTriple::new(stored_inner.clone())));
447
448 let ground_match = AlgebraTriplePattern::new(
450 TermPattern::NamedNode(nn("http://ex/a")),
451 TermPattern::NamedNode(nn("http://ex/b")),
452 TermPattern::NamedNode(nn("http://ex/c")),
453 );
454 let sp_match = SubjectPattern::QuotedTriple(Box::new(ground_match));
455 assert!(sp_match.matches(&subject));
456
457 let ground_diff = AlgebraTriplePattern::new(
459 TermPattern::NamedNode(nn("http://ex/x")),
460 TermPattern::NamedNode(nn("http://ex/y")),
461 TermPattern::NamedNode(nn("http://ex/z")),
462 );
463 let sp_diff = SubjectPattern::QuotedTriple(Box::new(ground_diff));
464 assert!(!sp_diff.matches(&subject));
465
466 let var_pat = AlgebraTriplePattern::new(
468 TermPattern::NamedNode(nn("http://ex/a")),
469 TermPattern::Variable(Variable::new("p").expect("valid var")),
470 TermPattern::Variable(Variable::new("o").expect("valid var")),
471 );
472 let sp_var = SubjectPattern::QuotedTriple(Box::new(var_pat));
473 assert!(sp_var.matches(&subject));
474
475 let object = Object::QuotedTriple(Box::new(QuotedTriple::new(stored_inner)));
477 let op_diff = ObjectPattern::QuotedTriple(Box::new(AlgebraTriplePattern::new(
478 TermPattern::NamedNode(nn("http://ex/x")),
479 TermPattern::NamedNode(nn("http://ex/y")),
480 TermPattern::NamedNode(nn("http://ex/z")),
481 )));
482 assert!(!op_diff.matches(&object));
483 }
484}