sophia_api/
quad.rs

1//! A quad expresses a single fact within a context.
2//! Quads are like RDF [`triples`](crate::triple)
3//! augmented with an optional graph name.
4//!
5//! They are the individual statements of an RDF [datasets](crate::dataset).
6use crate::term::matcher::{GraphNameMatcher, TermMatcher};
7use crate::term::{graph_name_eq, GraphName, Term};
8
9/// Type alias for terms borrowed from a quad.
10pub type QBorrowTerm<'a, T> = <<T as Quad>::Term as Term>::BorrowTerm<'a>;
11/// The typical structure representing a Quad of terms T
12pub type Spog<T> = ([T; 3], GraphName<T>);
13/// An alternative structure representing a Quad of terms T
14/// (useful when sorting first by graph is required)
15pub type Gspo<T> = (GraphName<T>, [T; 3]);
16
17/// This trait represents an abstract RDF quad,
18/// and provide convenient methods for working with quads.
19pub trait Quad: Sized {
20    /// The type of [`Term`] used by this quad when borrowing it
21    type Term: Term;
22
23    /// The subject of this quad.
24    fn s(&self) -> QBorrowTerm<Self>;
25
26    /// The predicate of this quad.
27    fn p(&self) -> QBorrowTerm<Self>;
28
29    /// The object of this quad.
30    fn o(&self) -> QBorrowTerm<Self>;
31
32    /// The graph name of this quad.
33    ///
34    /// `None` means that this quad belongs to the default graph of the dataset.
35    fn g(&self) -> GraphName<QBorrowTerm<Self>>;
36
37    /// The four components of this quad, as a quad of borrowed terms.
38    ///
39    /// See also [`Quad::to_spog`].
40    #[inline]
41    fn spog(&self) -> Spog<QBorrowTerm<Self>> {
42        ([self.s(), self.p(), self.o()], self.g())
43    }
44
45    /// Consume this quad, returning its subject.
46    fn to_s(self) -> Self::Term {
47        let [s, _, _] = self.to_spog().0;
48        s
49    }
50
51    /// Consume this quad, returning its predicate.
52    fn to_p(self) -> Self::Term {
53        let [_, p, _] = self.to_spog().0;
54        p
55    }
56
57    /// Consume this quad, returning its object.
58    fn to_o(self) -> Self::Term {
59        let [_, _, o] = self.to_spog().0;
60        o
61    }
62
63    /// Consume this quad, returning its graph name.
64    fn to_g(self) -> GraphName<Self::Term> {
65        self.to_spog().1
66    }
67
68    /// Consume this quad, returning all its components.
69    ///
70    /// See also [`Quad::spog`].
71    fn to_spog(self) -> Spog<Self::Term>;
72
73    /// Checks that the constituents terms of this quad match the respective matchers.
74    fn matched_by<S, P, O, G>(&self, sm: S, pm: P, om: O, gm: G) -> bool
75    where
76        S: TermMatcher,
77        P: TermMatcher,
78        O: TermMatcher,
79        G: GraphNameMatcher,
80    {
81        sm.matches(&self.s())
82            && pm.matches(&self.p())
83            && om.matches(&self.o())
84            && gm.matches(self.g().as_ref())
85    }
86
87    /// Check whether `other` is term-wise equal (using [`Term::eq`]) to `self`.
88    ///
89    /// See also [`eq_spog`](Quad::eq_spog), [`matched_by`](Quad::matched_by).
90    #[inline]
91    fn eq<T: Quad>(&self, other: T) -> bool {
92        self.eq_spog(other.s(), other.p(), other.o(), other.g())
93    }
94
95    /// Check whether the quad (`s`, `p`, `o`) is term-wise equal (using [`Term::eq`]) to `self`.
96    ///
97    /// See also [`eq`](Quad::eq), [`matched_by`](Quad::matched_by).
98    fn eq_spog<S: Term, P: Term, O: Term, G: Term>(
99        &self,
100        s: S,
101        p: P,
102        o: O,
103        g: GraphName<G>,
104    ) -> bool {
105        self.s().eq(s) && self.p().eq(p) && self.o().eq(o) && graph_name_eq(self.g(), g)
106    }
107
108    /// Convert this quad to a [`Triple`](crate::triple::Triple) (dropping the graph name)
109    ///
110    /// NB: if you do not wish to consume this quad,
111    /// you can combine this method with [`spog`](Quad::spog) as below:
112    /// ```
113    /// # use sophia_api::quad::Quad;
114    /// # use sophia_api::triple::Triple;
115    /// # fn test<T: Quad>(q: &T) -> impl Triple + '_ {
116    ///     q.spog().into_triple()   
117    /// # }
118    /// ```
119    fn into_triple(self) -> [Self::Term; 3] {
120        self.to_spog().0
121    }
122}
123
124impl<T: Term> Quad for [T; 4] {
125    type Term = T;
126
127    fn s(&self) -> QBorrowTerm<Self> {
128        self[0].borrow_term()
129    }
130    fn p(&self) -> QBorrowTerm<Self> {
131        self[1].borrow_term()
132    }
133    fn o(&self) -> QBorrowTerm<Self> {
134        self[2].borrow_term()
135    }
136    fn g(&self) -> GraphName<QBorrowTerm<Self>> {
137        Some(self[3].borrow_term())
138    }
139    fn to_s(self) -> Self::Term {
140        let [s, _, _, _] = self;
141        s
142    }
143    fn to_p(self) -> Self::Term {
144        let [_, p, _, _] = self;
145        p
146    }
147    fn to_o(self) -> Self::Term {
148        let [_, _, o, _] = self;
149        o
150    }
151    fn to_g(self) -> GraphName<Self::Term> {
152        let [_, _, _, g] = self;
153        Some(g)
154    }
155    fn to_spog(self) -> Spog<Self::Term> {
156        let [s, p, o, g] = self;
157        ([s, p, o], Some(g))
158    }
159}
160
161// Spog<T>
162impl<T: Term> Quad for ([T; 3], GraphName<T>) {
163    type Term = T;
164
165    fn s(&self) -> QBorrowTerm<Self> {
166        self.0[0].borrow_term()
167    }
168    fn p(&self) -> QBorrowTerm<Self> {
169        self.0[1].borrow_term()
170    }
171    fn o(&self) -> QBorrowTerm<Self> {
172        self.0[2].borrow_term()
173    }
174    fn g(&self) -> GraphName<QBorrowTerm<Self>> {
175        self.1.as_ref().map(|gn| gn.borrow_term())
176    }
177    fn to_s(self) -> Self::Term {
178        let [s, _, _] = self.0;
179        s
180    }
181    fn to_p(self) -> Self::Term {
182        let [_, p, _] = self.0;
183        p
184    }
185    fn to_o(self) -> Self::Term {
186        let [_, _, o] = self.0;
187        o
188    }
189    fn to_g(self) -> GraphName<Self::Term> {
190        self.1
191    }
192    fn to_spog(self) -> Spog<Self::Term> {
193        self
194    }
195}
196
197// Gspo<T>
198impl<T: Term> Quad for (GraphName<T>, [T; 3]) {
199    type Term = T;
200
201    fn s(&self) -> QBorrowTerm<Self> {
202        self.1[0].borrow_term()
203    }
204    fn p(&self) -> QBorrowTerm<Self> {
205        self.1[1].borrow_term()
206    }
207    fn o(&self) -> QBorrowTerm<Self> {
208        self.1[2].borrow_term()
209    }
210    fn g(&self) -> GraphName<QBorrowTerm<'_, Self>> {
211        self.0.as_ref().map(|gn| gn.borrow_term())
212    }
213    fn to_s(self) -> Self::Term {
214        let [s, _, _] = self.1;
215        s
216    }
217    fn to_p(self) -> Self::Term {
218        let [_, p, _] = self.1;
219        p
220    }
221    fn to_o(self) -> Self::Term {
222        let [_, _, o] = self.1;
223        o
224    }
225    fn to_g(self) -> GraphName<Self::Term> {
226        self.0
227    }
228    fn to_spog(self) -> Spog<Self::Term> {
229        let (g, spo) = self;
230        (spo, g)
231    }
232}
233
234/// Iter over all the components of a [`Quad`]
235pub fn iter_spog<T: Quad>(q: T) -> impl Iterator<Item = T::Term> {
236    let (spo, g) = q.to_spog();
237    spo.into_iter().chain(g)
238}
239
240#[cfg(test)]
241mod check_implementability {
242    use super::*;
243    use crate::term::*;
244    use mownstr::MownStr;
245
246    #[derive(Clone, Copy, Debug)]
247    struct MyBnode(usize);
248
249    impl Term for MyBnode {
250        type BorrowTerm<'x> = Self;
251
252        fn kind(&self) -> TermKind {
253            TermKind::BlankNode
254        }
255        fn bnode_id(&self) -> Option<BnodeId<MownStr>> {
256            Some(BnodeId::new_unchecked(MownStr::from(format!(
257                "b{}",
258                self.0
259            ))))
260        }
261        fn borrow_term(&self) -> Self::BorrowTerm<'_> {
262            *self
263        }
264    }
265
266    #[derive(Clone, Copy, Debug)]
267    struct MyQuad([usize; 4]);
268
269    impl Quad for MyQuad {
270        type Term = MyBnode;
271
272        fn s(&self) -> QBorrowTerm<Self> {
273            MyBnode(self.0[0])
274        }
275        fn p(&self) -> QBorrowTerm<Self> {
276            MyBnode(self.0[1])
277        }
278        fn o(&self) -> QBorrowTerm<Self> {
279            MyBnode(self.0[2])
280        }
281        fn g(&self) -> GraphName<QBorrowTerm<Self>> {
282            match self.0[3] {
283                0 => None,
284                n => Some(MyBnode(n)),
285            }
286        }
287        fn to_s(self) -> Self::Term {
288            self.s()
289        }
290        fn to_p(self) -> Self::Term {
291            self.p()
292        }
293        fn to_o(self) -> Self::Term {
294            self.o()
295        }
296        fn to_g(self) -> GraphName<Self::Term> {
297            self.g()
298        }
299        fn to_spog(self) -> Spog<Self::Term> {
300            ([self.s(), self.p(), self.o()], self.g())
301        }
302    }
303
304    #[allow(dead_code)] // only checks that this compiles
305    fn check_quad_impl(t: [SimpleTerm; 4]) {
306        fn foo<T: Quad>(t: T) {
307            println!("{:?}", t.s().kind());
308        }
309        let rt = t.spog();
310        foo(rt);
311        {
312            let rt2 = t.spog();
313            foo(rt2);
314        }
315        foo(rt);
316        foo(rt.spog());
317        foo(t);
318
319        let mt = MyQuad([1, 2, 3, 0]);
320        let rmt = mt.spog();
321        foo(rmt);
322        {
323            let rmt2 = mt.spog();
324            foo(rmt2);
325        }
326        foo(rmt);
327        foo(rmt.spog());
328        foo(mt);
329    }
330}
331
332#[cfg(test)]
333mod test_quad {
334    use super::*;
335    use crate::term::SimpleTerm;
336    use sophia_iri::IriRef;
337
338    const S: IriRef<&str> = IriRef::new_unchecked_const("tag:s");
339    const P: IriRef<&str> = IriRef::new_unchecked_const("tag:o");
340    const O: IriRef<&str> = IriRef::new_unchecked_const("tag:p");
341    const G: GraphName<IriRef<&str>> = Some(IriRef::new_unchecked_const("tag:g"));
342
343    #[test]
344    fn quad_matched_by() {
345        use crate::term::matcher::Any;
346        let q = ([S, P, O], G);
347
348        assert!(q.matched_by(Any, Any, Any, Any));
349        assert!(q.matched_by([S], [P], [O], [G]));
350        assert!(q.matched_by([O, S], [S, P], [P, O], [None, G]));
351        let istag = |t: SimpleTerm| t.iri().map(|iri| iri.starts_with("tag:")).unwrap_or(false);
352        assert!(q.matched_by(istag, istag, istag, istag.gn()));
353
354        let none: Option<IriRef<&str>> = None;
355        assert!(!q.matched_by(none, Any, Any, Any));
356        assert!(!q.matched_by(Any, none, Any, Any));
357        assert!(!q.matched_by(Any, Any, none, Any));
358        assert!(!q.matched_by(Any, Any, Any, none.gn()));
359        assert!(!q.matched_by([P, O], Any, Any, Any));
360        assert!(!q.matched_by(Any, [S, O], Any, Any));
361        assert!(!q.matched_by(Any, Any, [S, P], Any));
362        assert!(!q.matched_by(Any, Any, Any, [S, P, O].gn()));
363        let notag = |t: SimpleTerm| t.iri().map(|iri| !iri.starts_with("tag:")).unwrap_or(true);
364        assert!(!q.matched_by(notag, Any, Any, Any));
365        assert!(!q.matched_by(Any, notag, Any, Any));
366        assert!(!q.matched_by(Any, Any, notag, Any));
367        assert!(!q.matched_by(Any, Any, Any, notag.gn()));
368
369        let ts = vec![
370            ([S, P, S], G),
371            ([S, P, P], G),
372            ([S, P, O], G),
373            ([P, P, S], G),
374            ([P, P, P], G),
375            ([P, P, O], G),
376            ([O, P, S], G),
377            ([O, P, P], G),
378            ([O, P, O], G),
379            ([S, P, S], None),
380            ([P, P, P], None),
381            ([O, P, O], None),
382        ];
383        let c = ts
384            .iter()
385            .filter(|t| t.matched_by([S, O], Any, [S, O], Any))
386            .count();
387        assert_eq!(c, 6);
388
389        let default = G.filter(|_| false);
390        let c = ts
391            .iter()
392            .filter(|t| t.matched_by([S], Any, Any, [default]))
393            .count();
394        assert_eq!(c, 1);
395    }
396}