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