sophia_api/graph/
adapter.rs

1//! I define adapters for the [`graph`](super) related traits.
2use super::*;
3use crate::dataset::{DTerm, Dataset, MutableDataset, SetDataset};
4use crate::quad::Quad;
5use crate::term::{
6    matcher::{Any, GraphNameMatcher},
7    GraphName,
8};
9
10/// I wrap a [`Dataset`] as a [`Graph`]
11/// corresponding to the union of all graphs (default and named)
12/// from the original dataset.
13#[repr(transparent)]
14#[derive(Clone, Copy, Debug)]
15pub struct UnionGraph<T: Dataset>(T);
16
17impl<T: Dataset> UnionGraph<T> {
18    /// Wrap the given dataset as a the union of all its graphs.
19    pub fn new(wrapped: T) -> Self {
20        UnionGraph(wrapped)
21    }
22
23    /// Unwrap the inner [`Dataset`].
24    pub fn unwrap(self) -> T {
25        self.0
26    }
27}
28
29impl<T: Dataset> Graph for UnionGraph<T> {
30    type Triple<'x> = [DTerm<'x, T>; 3] where Self: 'x;
31    type Error = T::Error;
32
33    fn triples(&self) -> impl Iterator<Item = GResult<Self, Self::Triple<'_>>> + '_ {
34        self.0
35            .quads()
36            // NB: for some reason, .map_ok(...) below does not compile since 1.66 nightly
37            .map(|r| r.map(Quad::into_triple))
38    }
39
40    fn triples_matching<'s, S, P, O>(
41        &'s self,
42        sm: S,
43        pm: P,
44        om: O,
45    ) -> impl Iterator<Item = GResult<Self, Self::Triple<'s>>> + 's
46    where
47        S: TermMatcher + 's,
48        P: TermMatcher + 's,
49        O: TermMatcher + 's,
50    {
51        self.0
52            .quads_matching(sm, pm, om, Any)
53            // NB: for some reason, .map_ok(...) below does not compile since 1.66 nightly
54            .map(|r| r.map(Quad::into_triple))
55    }
56
57    fn subjects(&self) -> impl Iterator<Item = GResult<Self, GTerm<'_, Self>>> + '_ {
58        self.0.subjects()
59    }
60
61    fn predicates(&self) -> impl Iterator<Item = GResult<Self, GTerm<'_, Self>>> + '_ {
62        self.0.predicates()
63    }
64
65    fn objects(&self) -> impl Iterator<Item = GResult<Self, GTerm<'_, Self>>> + '_ {
66        self.0.objects()
67    }
68
69    fn iris(&self) -> impl Iterator<Item = GResult<Self, GTerm<'_, Self>>> + '_ {
70        self.0.iris()
71    }
72
73    fn blank_nodes(&self) -> impl Iterator<Item = GResult<Self, GTerm<'_, Self>>> + '_ {
74        self.0.blank_nodes()
75    }
76
77    fn literals(&self) -> impl Iterator<Item = GResult<Self, GTerm<'_, Self>>> + '_ {
78        self.0.literals()
79    }
80
81    fn quoted_triples<'s>(&'s self) -> Box<dyn Iterator<Item = GResult<Self, GTerm<'_, Self>>> + '_>
82    where
83        GTerm<'s, Self>: Clone,
84    {
85        self.0.quoted_triples()
86    }
87
88    fn variables(&self) -> impl Iterator<Item = GResult<Self, GTerm<'_, Self>>> + '_ {
89        self.0.variables()
90    }
91}
92
93//
94
95/// I wrap a [`Dataset`] as a [`Graph`]
96/// corresponding to the union of a subset of its graphs,
97/// determined by a [`GraphNameMatcher`].
98#[derive(Clone, Copy, Debug)]
99pub struct PartialUnionGraph<D: Dataset, M: GraphNameMatcher> {
100    d: D,
101    m: M,
102}
103
104impl<D: Dataset, M: GraphNameMatcher + Copy> PartialUnionGraph<D, M> {
105    /// Wrap the given dataset as a single [`Graph`]
106    /// (selected via the given graph name).
107    pub fn new(d: D, m: M) -> Self {
108        PartialUnionGraph { d, m }
109    }
110
111    /// Unwrap the inner [`GraphName`] and [`Dataset`].
112    pub fn unwrap(self) -> (D, M) {
113        (self.d, self.m)
114    }
115}
116
117impl<D: Dataset, M: GraphNameMatcher + Copy> Graph for PartialUnionGraph<D, M> {
118    type Triple<'x> = [DTerm<'x, D>; 3] where Self: 'x;
119    type Error = D::Error;
120
121    fn triples(&self) -> impl Iterator<Item = GResult<Self, Self::Triple<'_>>> + '_ {
122        self.d
123            .quads_matching(Any, Any, Any, self.m)
124            // NB: for some reason, .map_ok(...) below does not compile since 1.66 nightly
125            .map(|r| r.map(Quad::into_triple))
126    }
127
128    fn triples_matching<'s, S, P, O>(
129        &'s self,
130        sm: S,
131        pm: P,
132        om: O,
133    ) -> impl Iterator<Item = GResult<Self, Self::Triple<'s>>> + 's
134    where
135        S: TermMatcher + 's,
136        P: TermMatcher + 's,
137        O: TermMatcher + 's,
138    {
139        self.d
140            .quads_matching(sm, pm, om, self.m)
141            // NB: for some reason, .map_ok(...) below does not compile since 1.66 nightly
142            .map(|r| r.map(Quad::into_triple))
143    }
144}
145
146//
147
148/// I wrap a [`Dataset`] as a [`Graph`]
149/// corresponding to a specific graph (default or named) of the wrapped dataset.
150///
151/// This graph is also [mutable](MutableGraph) if the underlying dataset is.
152///
153/// NB: this type is design to be the return type of [`Dataset::graph`] and [`Dataset::graph_mut`].
154/// It is not designed to be usable "from scratch".
155#[derive(Clone, Copy, Debug)]
156pub struct DatasetGraph<D: Dataset, G: Term> {
157    d: D,
158    g: GraphName<G>,
159}
160
161impl<D: Dataset, G: Term> DatasetGraph<D, G> {
162    /// Wrap the given dataset as a single [`Graph`]
163    /// (selected via the given graph name).
164    pub fn new(d: D, g: GraphName<G>) -> Self {
165        DatasetGraph { d, g }
166    }
167
168    /// Unwrap the inner [`GraphName`] and [`Dataset`].
169    pub fn unwrap(self) -> (D, GraphName<G>) {
170        (self.d, self.g)
171    }
172
173    fn g(&self) -> GraphName<G::BorrowTerm<'_>> {
174        self.g.as_ref().map(|gn| gn.borrow_term())
175    }
176
177    fn gd(&mut self) -> (Option<G::BorrowTerm<'_>>, &mut D) {
178        (self.g.as_ref().map(|gn| gn.borrow_term()), &mut self.d)
179    }
180}
181
182impl<D: Dataset, G: Term> Graph for DatasetGraph<D, G> {
183    type Triple<'x> = [DTerm<'x, D>; 3] where Self: 'x;
184    type Error = D::Error;
185
186    fn triples(&self) -> impl Iterator<Item = GResult<Self, Self::Triple<'_>>> + '_ {
187        self.d
188            .quads_matching(Any, Any, Any, [self.g()])
189            // NB: for some reason, .map_ok(...) below does not compile since 1.66 nightly
190            .map(|r| r.map(Quad::into_triple))
191    }
192
193    fn triples_matching<'s, S, P, O>(
194        &'s self,
195        sm: S,
196        pm: P,
197        om: O,
198    ) -> impl Iterator<Item = GResult<Self, Self::Triple<'s>>> + 's
199    where
200        S: TermMatcher + 's,
201        P: TermMatcher + 's,
202        O: TermMatcher + 's,
203    {
204        self.d
205            .quads_matching(sm, pm, om, [self.g()])
206            // NB: for some reason, .map_ok(...) below does not compile since 1.66 nightly
207            .map(|r| r.map(Quad::into_triple))
208    }
209}
210
211impl<D: SetDataset, G: Term> SetGraph for DatasetGraph<D, G> {}
212
213impl<D: MutableDataset, G: Term> MutableGraph for DatasetGraph<D, G> {
214    type MutationError = D::MutationError;
215
216    fn insert<TS, TP, TO>(&mut self, s: TS, p: TP, o: TO) -> MgResult<Self, bool>
217    where
218        TS: Term,
219        TP: Term,
220        TO: Term,
221    {
222        let (g, d) = self.gd();
223        d.insert(s, p, o, g)
224    }
225
226    fn remove<TS, TP, TO>(&mut self, s: TS, p: TP, o: TO) -> MgResult<Self, bool>
227    where
228        TS: Term,
229        TP: Term,
230        TO: Term,
231    {
232        let (g, d) = self.gd();
233        d.remove(s, p, o, g)
234    }
235}
236
237#[cfg(test)]
238mod test {
239    use super::*;
240    use crate::quad::Spog;
241    use crate::term::{graph_name_eq, FromTerm};
242    use sophia_iri::Iri;
243    use std::collections::BTreeSet;
244
245    static G1: Iri<&'static str> = Iri::new_unchecked_const("http://example.com/g1");
246    static G2: Iri<&'static str> = Iri::new_unchecked_const("http://example.com/g2");
247    static G3: Iri<&'static str> = Iri::new_unchecked_const("http://example.com/g3");
248
249    type MyTerm = SimpleTerm<'static>;
250    type MyQuad = Spog<MyTerm>;
251    type MyDS = BTreeSet<MyQuad>;
252
253    #[derive(Clone, Copy, Debug)]
254    struct GM;
255    impl GraphNameMatcher for GM {
256        type Term = MyTerm;
257
258        fn matches<T2: Term + ?Sized>(&self, graph_name: GraphName<&T2>) -> bool {
259            graph_name_eq(graph_name.map(|gn| gn.borrow_term()), Some(G1))
260                || graph_name_eq(graph_name.map(|gn| gn.borrow_term()), Some(G2))
261        }
262    }
263
264    type MyUG = UnionGraph<MyDS>;
265    fn collect_union_graph<T: TripleSource>(mut ts: T) -> Result<MyUG, T::Error> {
266        let mut ds = MyDS::new();
267        ts.for_each_triple(|t| {
268            let [s, p, o] = t.spo();
269            ds.insert_quad(([s, p, o], Some(s))).unwrap();
270        })?;
271        Ok(ds.into_union_graph())
272    }
273    crate::test_immutable_graph_impl!(union_graph, MyUG, true, true, collect_union_graph);
274
275    type MyPUG = PartialUnionGraph<MyDS, GM>;
276    fn collect_partial_union_graph<T: TripleSource>(mut ts: T) -> Result<MyPUG, T::Error> {
277        let g1: GraphName<MyTerm> = Some(G1.into_term());
278        let g2: GraphName<MyTerm> = Some(G2.into_term());
279        let g3: GraphName<MyTerm> = Some(G3.into_term());
280        let mut ds = MyDS::new();
281        let mut b = true;
282        ts.for_each_triple(|t| {
283            let [s, p, o] = t.spo();
284            if b {
285                ds.insert_quad(([s, p, o].map(MyTerm::from_term), g1.clone()))
286                    .unwrap();
287                ds.insert_quad(([o, p, s].map(MyTerm::from_term), g3.clone()))
288                    .unwrap();
289            } else {
290                ds.insert_quad(([s, p, o].map(MyTerm::from_term), g2.clone()))
291                    .unwrap();
292            }
293            b = !b;
294        })?;
295        Ok(PartialUnionGraph::new(ds, GM))
296    }
297    crate::test_immutable_graph_impl!(
298        partial_union_graph,
299        MyPUG,
300        true,
301        true,
302        collect_partial_union_graph
303    );
304
305    type MyDG = DatasetGraph<MyDS, MyTerm>;
306    fn collect_dataset_graph<T: TripleSource>(mut ts: T) -> Result<MyDG, T::Error> {
307        let g1: GraphName<MyTerm> = Some(G1.into_term());
308        let g2: GraphName<MyTerm> = Some(G2.into_term());
309        let mut ds = MyDS::new();
310        ts.for_each_triple(|t| {
311            let [s, p, o] = t.spo();
312            ds.insert_quad(([s, p, o].map(MyTerm::from_term), g1.clone()))
313                .unwrap();
314            ds.insert_quad(([o, p, s].map(MyTerm::from_term), g2.clone()))
315                .unwrap();
316        })?;
317        Ok(DatasetGraph::new(ds, g1))
318    }
319    crate::test_graph_impl!(dataset_graph, MyDG, true, true, collect_dataset_graph);
320
321    #[allow(dead_code)] // just check this compiles
322    fn check_trait_impls() {
323        let mut ds = MyDS::new();
324        let g1: Option<MyTerm> = Some(G1.into_term());
325
326        // check that Dataset::graph returns a Graph
327        for _ in ds.graph(g1.clone()).triples() {}
328
329        let mut gm = ds.graph_mut(g1);
330        // check that Dataset::graph_mut returns a Graph
331        for _ in gm.triples() {}
332        // check that Dataset::graph_mut returns a MutableGraph
333        gm.remove_triple([1, 2, 3]).unwrap();
334
335        // check that Dataset::partial_union_graph returns a Graph
336        for _ in ds.partial_union_graph(GM).triples() {}
337
338        // check that Dataset::union_graph returns a Graph
339        for _ in ds.union_graph().triples() {}
340
341        // check that Dataset::into_union_graph returns a Graph
342        for _ in ds.into_union_graph().triples() {}
343    }
344}