1use crate::quad::Spog;
20use crate::term::{matcher::TermMatcher, GraphName, Term};
21
22pub type TBorrowTerm<'a, T> = <<T as Triple>::Term as Term>::BorrowTerm<'a>;
24
25pub trait Triple {
28 type Term: Term;
30
31 fn s(&self) -> TBorrowTerm<Self>;
33
34 fn p(&self) -> TBorrowTerm<Self>;
36
37 fn o(&self) -> TBorrowTerm<Self>;
39
40 #[inline]
44 fn spo(&self) -> [TBorrowTerm<Self>; 3] {
45 [self.s(), self.p(), self.o()]
46 }
47
48 fn to_s(self) -> Self::Term
50 where
51 Self: Sized,
52 {
53 let [s, _, _] = self.to_spo();
54 s
55 }
56
57 fn to_p(self) -> Self::Term
59 where
60 Self: Sized,
61 {
62 let [_, p, _] = self.to_spo();
63 p
64 }
65
66 fn to_o(self) -> Self::Term
68 where
69 Self: Sized,
70 {
71 let [_, _, o] = self.to_spo();
72 o
73 }
74
75 fn to_spo(self) -> [Self::Term; 3]
79 where
80 Self: Sized;
81
82 fn matched_by<S, P, O>(&self, sm: S, pm: P, om: O) -> bool
84 where
85 S: TermMatcher,
86 P: TermMatcher,
87 O: TermMatcher,
88 {
89 sm.matches(&self.s()) && pm.matches(&self.p()) && om.matches(&self.o())
90 }
91
92 #[inline]
96 fn eq<T: Triple>(&self, other: T) -> bool {
97 self.eq_spo(other.s(), other.p(), other.o())
98 }
99
100 fn eq_spo<S: Term, P: Term, O: Term>(&self, s: S, p: P, o: O) -> bool {
104 self.s().eq(s) && self.p().eq(p) && self.o().eq(o)
105 }
106
107 fn into_quad(self) -> Spog<Self::Term>
121 where
122 Self: Sized,
123 {
124 (self.to_spo(), None)
125 }
126
127 fn into_quad_from(self, graph_name: GraphName<Self::Term>) -> Spog<Self::Term>
131 where
132 Self: Sized,
133 {
134 (self.to_spo(), graph_name)
135 }
136}
137
138impl<T: Term> Triple for [T; 3] {
139 type Term = T;
140
141 fn s(&self) -> TBorrowTerm<Self> {
142 self[0].borrow_term()
143 }
144 fn p(&self) -> TBorrowTerm<Self> {
145 self[1].borrow_term()
146 }
147 fn o(&self) -> TBorrowTerm<Self> {
148 self[2].borrow_term()
149 }
150 fn to_spo(self) -> [Self::Term; 3] {
151 self
152 }
153}
154
155#[cfg(test)]
156mod check_implementability {
157 use super::*;
158 use crate::term::*;
159 use mownstr::MownStr;
160
161 #[derive(Clone, Copy, Debug)]
162 struct MyBnode(usize);
163
164 impl Term for MyBnode {
165 type BorrowTerm<'x> = Self;
166
167 fn kind(&self) -> TermKind {
168 TermKind::BlankNode
169 }
170 fn bnode_id(&self) -> Option<BnodeId<MownStr>> {
171 Some(BnodeId::new_unchecked(MownStr::from(format!(
172 "b{}",
173 self.0
174 ))))
175 }
176 fn borrow_term(&self) -> Self::BorrowTerm<'_> {
177 *self
178 }
179 }
180
181 #[derive(Clone, Copy, Debug)]
182 struct MyTriple([usize; 3]);
183
184 impl Triple for MyTriple {
185 type Term = MyBnode;
186
187 fn s(&self) -> TBorrowTerm<Self> {
188 MyBnode(self.0[0])
189 }
190 fn p(&self) -> TBorrowTerm<Self> {
191 MyBnode(self.0[1])
192 }
193 fn o(&self) -> TBorrowTerm<Self> {
194 MyBnode(self.0[2])
195 }
196 fn to_s(self) -> Self::Term {
197 self.s()
198 }
199 fn to_p(self) -> Self::Term {
200 self.p()
201 }
202 fn to_o(self) -> Self::Term {
203 self.o()
204 }
205 fn to_spo(self) -> [Self::Term; 3] {
206 [self.s(), self.p(), self.o()]
207 }
208 }
209
210 #[allow(dead_code)] fn check_triple_impl(t: [SimpleTerm; 3]) {
212 fn foo<T: Triple>(t: T) {
213 println!("{:?}", t.s().kind());
214 }
215 let rt = t.spo();
216 foo(rt);
217 {
218 let rt2 = t.spo();
219 foo(rt2);
220 }
221 foo(rt);
222 foo(rt.spo());
223 foo(t);
224
225 let mt = MyTriple([1, 2, 3]);
226 let rmt = mt.spo();
227 foo(rmt);
228 {
229 let rmt2 = mt.spo();
230 foo(rmt2);
231 }
232 foo(rmt);
233 foo(rmt.spo());
234 foo(mt);
235 }
236}
237
238#[cfg(test)]
239mod test_triple {
240 use super::*;
241 use crate::term::SimpleTerm;
242 use sophia_iri::IriRef;
243
244 const S: IriRef<&str> = IriRef::new_unchecked_const("tag:s");
245 const P: IriRef<&str> = IriRef::new_unchecked_const("tag:o");
246 const O: IriRef<&str> = IriRef::new_unchecked_const("tag:p");
247
248 #[test]
249 fn triple_matched_by() {
250 use crate::term::matcher::Any;
251 let t = [S, P, O];
252
253 assert!(t.matched_by(Any, Any, Any));
254 assert!(t.matched_by([S], [P], [O]));
255 assert!(t.matched_by([O, S], [S, P], [P, O]));
256 let istag = |t: SimpleTerm| t.iri().map(|iri| iri.starts_with("tag:")).unwrap_or(false);
257 assert!(t.matched_by(istag, istag, istag));
258
259 let none: Option<IriRef<&str>> = None;
260 assert!(!t.matched_by(none, Any, Any));
261 assert!(!t.matched_by(Any, none, Any));
262 assert!(!t.matched_by(Any, Any, none));
263 assert!(!t.matched_by([P, O], Any, Any));
264 assert!(!t.matched_by(Any, [S, O], Any));
265 assert!(!t.matched_by(Any, Any, [S, P]));
266 let notag = |t: SimpleTerm| t.iri().map(|iri| !iri.starts_with("tag:")).unwrap_or(true);
267 assert!(!t.matched_by(notag, Any, Any));
268 assert!(!t.matched_by(Any, notag, Any));
269 assert!(!t.matched_by(Any, Any, notag));
270
271 let ts = vec![
272 [S, P, S],
273 [S, P, P],
274 [S, P, O],
275 [P, P, S],
276 [P, P, P],
277 [P, P, O],
278 [O, P, S],
279 [O, P, P],
280 [O, P, O],
281 ];
282 let c = ts
283 .iter()
284 .filter(|t| t.matched_by([S, O], Any, [S, O]))
285 .count();
286 assert_eq!(c, 4);
287 }
288}