sophia_api/source/
_quad.rs

1use super::*;
2use crate::dataset::{CollectibleDataset, Dataset, MutableDataset};
3use crate::quad::Quad;
4
5/// A quad source produces [quads](Quad), and may also fail in the process.
6///
7/// see [module documentation](super) for the rationale of his trait.
8///
9/// # Common implementors
10///
11/// Any iterator yielding [results](std::result::Result) of [`Quad`]
12/// implements the [`QuadSource`] trait.
13///
14/// Any iterator of [`Quad`] can also be converted to an [`Infallible`] [`QuadSource`]
15/// thanks to the [`IntoQuadSource`] extension trait.
16pub trait QuadSource {
17    /// The type of quads this source yields.
18    type Quad<'x>: Quad;
19    /// The type of errors produced by this source.
20    type Error: Error + 'static;
21
22    /// Call f for some quad(s) (possibly zero) from this source, if any.
23    ///
24    /// Return `Ok(false)` if there are no more quads in this source.
25    ///
26    /// Return an error if either the source or `f` errs.
27    fn try_for_some_quad<E, F>(&mut self, f: F) -> StreamResult<bool, Self::Error, E>
28    where
29        E: Error,
30        F: FnMut(Self::Quad<'_>) -> Result<(), E>;
31
32    /// Call f for all quads from this source.
33    ///
34    /// Return an error if either the source or `f` errs.
35    #[inline]
36    fn try_for_each_quad<F, E>(&mut self, mut f: F) -> StreamResult<(), Self::Error, E>
37    where
38        F: FnMut(Self::Quad<'_>) -> Result<(), E>,
39        E: Error,
40    {
41        while self.try_for_some_quad(&mut f)? {}
42        Ok(())
43    }
44
45    /// Call f for some quad(s) (possibly zero) from this source, if any.
46    ///
47    /// Return false if there are no more quads in this source.
48    ///
49    /// Return an error if either the source errs.
50    #[inline]
51    fn for_some_quad<F>(&mut self, f: &mut F) -> Result<bool, Self::Error>
52    where
53        F: FnMut(Self::Quad<'_>),
54    {
55        self.try_for_some_quad(|t| -> Result<(), Self::Error> {
56            f(t);
57            Ok(())
58        })
59        .map_err(StreamError::inner_into)
60    }
61
62    /// Call f for all quads from this source.
63    ///
64    /// Return an error if either the source errs.
65    #[inline]
66    fn for_each_quad<F>(&mut self, f: F) -> Result<(), Self::Error>
67    where
68        F: FnMut(Self::Quad<'_>),
69    {
70        let mut f = f;
71        while self.for_some_quad(&mut f)? {}
72        Ok(())
73    }
74
75    /// Returns a source which uses `predicate` to determine if an quad should be yielded.
76    #[inline]
77    fn filter_quads<F>(self, predicate: F) -> filter::FilterQuadSource<Self, F>
78    where
79        Self: Sized,
80        F: FnMut(&Self::Quad<'_>) -> bool,
81    {
82        filter::FilterQuadSource {
83            source: self,
84            predicate,
85        }
86    }
87
88    /// Returns a source that both filters and maps.
89    ///
90    /// See also [`QuadSource::filter_quads`] and [`QuadSource::map_quads`].
91    #[inline]
92    fn filter_map_quads<F, T>(self, filter_map: F) -> filter_map::FilterMapQuadSource<Self, F>
93    where
94        Self: Sized,
95        F: FnMut(Self::Quad<'_>) -> Option<T>,
96    {
97        filter_map::FilterMapQuadSource {
98            source: self,
99            filter_map,
100        }
101    }
102
103    /// Returns a source which yield the result of `map` for each quad.
104    ///
105    /// See also [`QuadSource::to_triples`].
106    ///
107    /// NB: due to [some limitations in GATsĀ (Generic) Associated Types](https://blog.rust-lang.org/2022/10/28/gats-stabilization.html),
108    /// the `map` function is currently restricted in what it can return.
109    /// In particular, passing functions as trivial as `|q| q` or `|q| q.to_spog()`
110    /// currently do not compile on all implementations of [`QuadSource`].
111    /// Furthermore, some functions returning a [`Quad`] are accepted,
112    /// but fail to make the resulting [`map::MapQuadSource`] recognized as a [`QuadSource`].
113    ///
114    /// As a rule of thumb,
115    /// whenever `map` returns something satisfying the `'static` lifetime,
116    /// things should work as expected.
117    #[inline]
118    fn map_quads<F, T>(self, map: F) -> map::MapQuadSource<Self, F>
119    where
120        Self: Sized,
121        F: FnMut(Self::Quad<'_>) -> T,
122    {
123        map::MapQuadSource { source: self, map }
124    }
125
126    /// Convert of quads in this source to triples (stripping the graph name).
127    fn to_triples(self) -> convert::ToTriples<Self>
128    where
129        Self: Sized,
130    {
131        convert::ToTriples(self)
132    }
133
134    /// Returns the bounds on the remaining length of the source.
135    ///
136    /// This method has the same contract as [`Iterator::size_hint`].
137    fn size_hint_quads(&self) -> (usize, Option<usize>) {
138        (0, None)
139    }
140
141    /// Collect these quads into a new dataset.
142    #[inline]
143    fn collect_quads<D>(self) -> StreamResult<D, Self::Error, <D as Dataset>::Error>
144    where
145        Self: Sized,
146        for<'x> Self::Quad<'x>: Quad,
147        D: CollectibleDataset,
148    {
149        D::from_quad_source(self)
150    }
151
152    /// Insert all quads from this source into the given [MutableDataset].
153    ///
154    /// Stop on the first error (in the source or in the dataset).
155    #[inline]
156    fn add_to_dataset<D: MutableDataset>(
157        self,
158        dataset: &mut D,
159    ) -> StreamResult<usize, Self::Error, <D as MutableDataset>::MutationError>
160    where
161        Self: Sized,
162        for<'x> Self::Quad<'x>: Quad,
163    {
164        dataset.insert_all(self)
165    }
166}
167
168impl<'a, I, T, E> QuadSource for I
169where
170    I: Iterator<Item = Result<T, E>> + 'a,
171    T: Quad,
172    E: Error + 'static,
173{
174    type Quad<'x> = T;
175    type Error = E;
176
177    fn try_for_some_quad<E2, F>(&mut self, mut f: F) -> StreamResult<bool, Self::Error, E2>
178    where
179        E2: Error,
180        F: FnMut(Self::Quad<'_>) -> Result<(), E2>,
181    {
182        match self.next() {
183            Some(Err(e)) => Err(SourceError(e)),
184            Some(Ok(t)) => {
185                f(t).map_err(SinkError)?;
186                Ok(true)
187            }
188            None => Ok(false),
189        }
190    }
191
192    fn size_hint_quads(&self) -> (usize, Option<usize>) {
193        self.size_hint()
194    }
195}
196
197/// An extension trait for iterators,
198/// converting them to an [`Infallible`] [`QuadSource`].
199pub trait IntoQuadSource: Iterator + Sized {
200    /// Convert this iterator into an [`Infallible`] [`QuadSource`].
201    #[allow(clippy::type_complexity)]
202    fn into_quad_source(
203        self,
204    ) -> std::iter::Map<
205        Self,
206        fn(<Self as Iterator>::Item) -> Result<<Self as Iterator>::Item, Infallible>,
207    > {
208        self.map(Ok::<_, Infallible>)
209    }
210}
211
212impl<I> IntoQuadSource for I
213where
214    I: Iterator,
215    I::Item: Quad,
216{
217}
218
219#[cfg(test)]
220mod check_quad_source {
221    use super::*;
222    use crate::quad::Spog;
223    use crate::term::{SimpleTerm, Term};
224    use sophia_iri::IriRef;
225    use std::convert::Infallible;
226    use std::fmt::Write;
227
228    #[allow(dead_code)] // only checks that this compiles
229    pub fn check_for_each<TS>(ts: TS)
230    where
231        TS: QuadSource,
232    {
233        ts.filter_quads(|t| t.s().is_iri())
234            .for_each_quad(|t| println!("{:?}", t.s()))
235            .unwrap();
236    }
237
238    #[allow(dead_code)] // only checks that this compiles
239    fn check_quad_source_impl_generic_dataset<D: Dataset>(d: &D) {
240        d.quads()
241            .filter_quads(|t| t.s().is_iri())
242            .for_each_quad(|t| println!("{:?}", t.s()))
243            .unwrap();
244    }
245
246    #[allow(dead_code)] // only checks that this compiles
247    fn check_quad_source_impl_concrete_dataset(d: &[Spog<SimpleTerm>]) {
248        d.quads()
249            .filter_quads(|t| t.s().is_iri())
250            .for_each_quad(|t| println!("{:?}", t.s()))
251            .unwrap();
252    }
253
254    // checking that QuadSource can be implemented
255    struct DummyParser<'a> {
256        tokens: &'a [usize],
257        pos: usize,
258        buffers: [String; 3],
259    }
260
261    impl<'a> QuadSource for DummyParser<'a> {
262        type Quad<'x> = Spog<SimpleTerm<'x>>;
263        type Error = Infallible;
264
265        fn try_for_some_quad<E2, F>(&mut self, mut f: F) -> StreamResult<bool, Self::Error, E2>
266        where
267            E2: Error,
268            F: FnMut(Self::Quad<'_>) -> Result<(), E2>,
269        {
270            if self.tokens.len() - self.pos < 3 {
271                Ok(false)
272            } else {
273                for i in 0..3 {
274                    write!(&mut self.buffers[i], "b{}", self.tokens[self.pos + i]).unwrap();
275                }
276                let q = (
277                    [
278                        IriRef::new_unchecked(&self.buffers[0][..]).into_term(),
279                        IriRef::new_unchecked(&self.buffers[1][..]).into_term(),
280                        IriRef::new_unchecked(&self.buffers[2][..]).into_term(),
281                    ],
282                    None,
283                );
284                f(q).map_err(SinkError).map(|_| true)
285            }
286        }
287    }
288
289    #[allow(dead_code)] // only checks that this compiles
290    fn check_quad_source_impl_by_iterator(v: Vec<Result<Spog<SimpleTerm>, std::io::Error>>) {
291        v.into_iter()
292            .for_each_quad(|q| println!("{:?}", q.s()))
293            .unwrap();
294    }
295
296    /* Currently does not work...
297    #[allow(dead_code)]
298    fn check_quad_source_map<S: QuadSource>(mut qs: S) {
299        qs.for_each_quad(|q| println!("{:?}", q.s())).unwrap();
300        qs.map_quads(|q| q)
301            .for_each_quad(|q| println!("{:?}", q.s()))
302            .unwrap();
303    }
304    */
305}