sophia_api/source/
_triple.rs

1use std::error::Error;
2
3use super::*;
4use crate::graph::{CollectibleGraph, Graph, MutableGraph};
5use crate::triple::Triple;
6
7/// A triple source is a [`Source`] producing [triples](Triple).
8///
9/// This trait extends the [`Source`] trait with triple-specific methods.
10///
11/// It does not need to be explicitly implemented:
12/// any [`Source`] implementation producing [triples](Triple)
13/// will automatically implement [`TripleSource`].
14///
15/// See also [`TSTriple`].
16pub trait TripleSource: Source + IsTripleSource {
17    /// Call f for some triple(s) (possibly zero) from this source, if any.
18    ///
19    /// Return `Ok(false)` if there are no more triples in this source.
20    ///
21    /// Return an error if either the source or `f` errs.
22    #[inline]
23    fn try_for_some_triple<E, F>(&mut self, mut f: F) -> StreamResult<bool, Self::Error, E>
24    where
25        E: Error + Send + Sync + 'static,
26        F: FnMut(TSTriple<Self>) -> Result<(), E>,
27    {
28        self.try_for_some_item(|i| f(Self::i2t(i)))
29    }
30
31    /// Call f for all triples from this source.
32    ///
33    /// Return an error if either the source or `f` errs.
34    #[inline]
35    fn try_for_each_triple<F, E>(&mut self, mut f: F) -> StreamResult<(), Self::Error, E>
36    where
37        F: FnMut(TSTriple<Self>) -> Result<(), E>,
38        E: Error + Send + Sync + 'static,
39    {
40        self.try_for_each_item(|i| f(Self::i2t(i)))
41    }
42
43    /// Call f for some triple(s) (possibly zero) from this source, if any.
44    ///
45    /// Return false if there are no more triples in this source.
46    ///
47    /// Return an error if either the source errs.
48    #[inline]
49    fn for_some_triple<F>(&mut self, mut f: F) -> Result<bool, Self::Error>
50    where
51        F: FnMut(TSTriple<Self>),
52    {
53        self.for_some_item(|i| f(Self::i2t(i)))
54    }
55
56    /// Call f for all triples from this source.
57    ///
58    /// Return an error if either the source errs.
59    #[inline]
60    fn for_each_triple<F>(&mut self, mut f: F) -> Result<(), Self::Error>
61    where
62        F: FnMut(TSTriple<Self>),
63    {
64        self.for_each_item(|i| f(Self::i2t(i)))
65    }
66
67    /// Returns a source which uses `predicate` to determine if an triple should be yielded.
68    #[inline]
69    fn filter_triples<'f, F>(
70        self,
71        mut predicate: F,
72    ) -> filter::FilterTripleSource<Self, impl FnMut(&Self::Item<'_>) -> bool + 'f>
73    where
74        Self: Sized,
75        F: FnMut(&TSTriple<Self>) -> bool + 'f,
76    {
77        filter::FilterTripleSource(self.filter_items(move |i| predicate(Self::ri2t(i))))
78    }
79
80    /// Returns a source that both filters and maps.
81    ///
82    /// See also [`TripleSource::filter_triples`] and [`TripleSource::map_triples`].
83    #[inline]
84    fn filter_map_triples<'f, F, T>(
85        self,
86        mut filter_map: F,
87    ) -> filter_map::FilterMapSource<Self, impl FnMut(Self::Item<'_>) -> Option<T> + 'f>
88    where
89        Self: Sized,
90        F: FnMut(TSTriple<Self>) -> Option<T> + 'f,
91    {
92        self.filter_map_items(move |i| filter_map(Self::i2t(i)))
93    }
94
95    /// Returns a source which yield the result of `map` for each triple.
96    ///
97    /// See also [`TripleSource::to_quads`].
98    ///
99    /// NB: due to [some limitations in GATsĀ (Generic) Associated Types](https://blog.rust-lang.org/2022/10/28/gats-stabilization.html),
100    /// the `map` function is currently restricted in what it can return.
101    /// In particular, passing functions as trivial as `|t| t` or `|t| t.to_spo()`
102    /// currently do not compile on all implementations of [`TripleSource`].
103    /// Furthermore, some functions returning a [`Triple`] are accepted,
104    /// but fail to make the resulting [`map::MapSource`] recognized as a [`TripleSource`].
105    ///
106    /// As a rule of thumb,
107    /// whenever `map` returns something satisfying the `'static` lifetime,
108    /// things should work as expected.
109    #[inline]
110    fn map_triples<'m, F, T>(
111        self,
112        mut map: F,
113    ) -> map::MapSource<Self, impl FnMut(Self::Item<'_>) -> T + 'm>
114    where
115        Self: Sized,
116        F: FnMut(TSTriple<Self>) -> T + 'm,
117    {
118        self.map_items(move |i| map(Self::i2t(i)))
119    }
120
121    /// Returns the bounds on the remaining length of the source.
122    ///
123    /// This method has the same contract as [`Iterator::size_hint`].
124    fn size_hint_triples(&self) -> (usize, Option<usize>) {
125        self.size_hint_items()
126    }
127
128    /// Convert of triples in this source to quads (belonging to the default graph).
129    #[inline]
130    fn to_quads(self) -> convert::ToQuads<Self>
131    where
132        Self: Sized,
133    {
134        convert::ToQuads(self)
135    }
136
137    /// Collect these triples into a new graph.
138    #[inline]
139    fn collect_triples<G>(self) -> StreamResult<G, Self::Error, <G as Graph>::Error>
140    where
141        Self: Sized,
142        G: CollectibleGraph,
143    {
144        G::from_triple_source(self)
145    }
146
147    /// Insert all triples from this source into the given [MutableGraph].
148    ///
149    /// Stop on the first error (in the source or in the graph).
150    #[inline]
151    fn add_to_graph<G: MutableGraph>(
152        self,
153        graph: &mut G,
154    ) -> StreamResult<usize, Self::Error, <G as MutableGraph>::MutationError>
155    where
156        Self: Sized,
157    {
158        graph.insert_all(self)
159    }
160}
161
162/// Ensures that TripleSource acts as an type alias for any Source satisfying the conditions.
163impl<T> TripleSource for T where T: Source + IsTripleSource {}
164
165/// Type alias to denote the type of triples yielded by a [`TripleSource`].
166///
167/// **Why not using `TS::Item<'a>` instead?**
168/// [`Source::Item`] being a generic associated type (GAT),
169/// the compiler will not always "know" that `TS::Item<'a>` implements the [`Triple`] trait.
170/// This type alias, on the other hand, will always be recognized as a [`Triple`] implementation.
171pub type TSTriple<'a, TS> = <TS as IsTripleSource>::Triple<'a>;
172
173mod sealed {
174    use super::*;
175
176    pub trait IsTripleSource: Source {
177        type Triple<'x>: Triple;
178        fn i2t(i: Self::Item<'_>) -> Self::Triple<'_>;
179        fn ri2t<'a, 'b>(i: &'a Self::Item<'b>) -> &'a Self::Triple<'b>;
180    }
181
182    impl<TS> IsTripleSource for TS
183    where
184        TS: Source,
185        for<'x> TS::Item<'x>: Triple,
186    {
187        type Triple<'x> = Self::Item<'x>;
188
189        fn i2t(i: Self::Item<'_>) -> Self::Triple<'_> {
190            i
191        }
192
193        fn ri2t<'a, 'b>(i: &'a Self::Item<'b>) -> &'a Self::Triple<'b> {
194            i
195        }
196    }
197}
198use sealed::IsTripleSource;
199
200#[cfg(test)]
201mod check_triple_source {
202    use super::*;
203    use crate::term::{SimpleTerm, Term};
204    use sophia_iri::IriRef;
205    use std::convert::Infallible;
206    use std::fmt::Write;
207
208    #[allow(dead_code)] // only checks that this compiles
209    pub fn check_for_each<TS>(ts: TS)
210    where
211        TS: Source + TripleSource,
212    {
213        ts.filter_triples(|t| t.s().is_iri())
214            .for_each_triple(|t| println!("{:?}", t.s()))
215            .unwrap();
216    }
217
218    #[allow(dead_code)] // only checks that this compiles
219    fn check_triple_source_impl_generic_graph<G: Graph>(g: &G) {
220        g.triples()
221            .filter_triples(|t| t.s().is_iri())
222            .for_each_triple(|t| println!("{:?}", t.s()))
223            .unwrap();
224    }
225
226    #[allow(dead_code)] // only checks that this compiles
227    fn check_triple_source_impl_concrete_graph(g: &[[SimpleTerm; 3]]) {
228        g.triples()
229            .filter_triples(|t| t.s().is_iri())
230            .for_each_triple(|t| println!("{:?}", t.s()))
231            .unwrap();
232    }
233
234    // checking that TripleSource can be implemented
235    struct DummyParser<'a> {
236        tokens: &'a [usize],
237        pos: usize,
238        buffers: [String; 3],
239    }
240
241    impl<'a> Source for DummyParser<'a> {
242        type Item<'x> = [SimpleTerm<'x>; 3];
243        type Error = Infallible;
244
245        fn try_for_some_item<E2, F>(&mut self, mut f: F) -> StreamResult<bool, Self::Error, E2>
246        where
247            E2: Error,
248            F: FnMut(Self::Item<'_>) -> Result<(), E2>,
249        {
250            if self.tokens.len() - self.pos < 3 {
251                Ok(false)
252            } else {
253                for i in 0..3 {
254                    write!(&mut self.buffers[i], "b{}", self.tokens[self.pos + i]).unwrap();
255                }
256                let t = [
257                    IriRef::new_unchecked(&self.buffers[0][..]).into_term(),
258                    IriRef::new_unchecked(&self.buffers[1][..]).into_term(),
259                    IriRef::new_unchecked(&self.buffers[2][..]).into_term(),
260                ];
261                f(t).map_err(SinkError).map(|_| true)
262            }
263        }
264    }
265
266    #[allow(dead_code)] // only checks that this compiles
267    fn check_triple_source_impl_by_iterator(v: Vec<Result<[SimpleTerm; 3], std::io::Error>>) {
268        v.into_iter()
269            .for_each_triple(|t| println!("{:?}", t.s()))
270            .unwrap();
271    }
272}