sophia_api/
source.rs

1//! A [`Source`] yields items, and may also fail in the process.
2//! This module also provides two specialized kinds of source:
3//! [`TripleSource`] and [`QuadSource`].
4//!
5//! The [`Source`] trait provides an API similar to (a subset of) the [`Iterator`] API,
6//! with methods such as [`for_each_item`] and [`try_for_each_item`].
7//! [`TripleSource`] and [`QuadSource`] provides specialized alternative
8//! (e.g. [`for_each_triple`] and [`for_each_quad`], respectively).
9//!
10//! # Rationale (or Why not simply use `Iterator`?)
11//!
12//! The [`Iterator`] trait is designed in such a way that items must live at least as long as the iterator itself.
13//! This assumption may be too strong in some situations.
14//!
15//! For example,
16//! consider a parser using 3 buffers to store the subject, predicate,
17//! and object of the triples it parses.
18//! Each time it extracts a triple from the data,
19//! it yields it (as 3 references to its internal buffers)
20//! to the closure passed to `for_each_triple`.
21//! Then, it **reuses** the buffers to store the data for the next triple,
22//! and yields the new triple, as 3 references to the *same* buffers.
23//!
24//! Such a parser can not implement [`Iterator`],
25//! because, once yielded by the iterator's `next` method,
26//! an item is free to live during further iterations.
27//! In particular, it can be stored in a collection,
28//! and still be alive when the `next` method is called again
29//! (consider for example the [`Iterator::collect`] method).
30//!
31//! Because many parsers (as well as other triple/quad sources)
32//! will be implemented in a manner similar to that described above,
33//! we have to provide a trait with *weaker assumptions*
34//! on the lifetime of the yielded triples.
35//!
36//! The alternative would be to wrap such parsers with a layer that would *copy*
37//! the data from internal buffers to fresh buffers for each triples,
38//! but we do not want to impose that cost on all implementations
39//! — especially when many consumers will be happy with short-lived references.
40//!
41//! # About the design of [`TripleSource`] and [`QuadSource`]
42//!
43//! The design of [`TripleSource`] (resp. [`QuadSource`]) may seem overcomplicated,
44//! but it aims to have the following properties.
45//!
46//! * When a concrete type implements [`Source`] and its items implement
47//!   [`Triple`](crate::triple::Triple), then it is automatically recognized as a
48//!   [`TripleSource`].
49//! * When a [`TripleSource`] is required, it can be used as a simple trait bound,
50//!   without requiring the user to add a higher ranked trait bound (HRTB) like
51//!   `for <'x> S:Item<'x>: Triple`.
52//!
53//! [`for_each_item`]: Source::for_each_item
54//! [`try_for_each_item`]: Source::try_for_each_item
55//! [`for_each_triple`]: TripleSource::for_each_triple
56//! [`for_each_quad`]: QuadSource::for_each_quad
57//! [higher-rank trait bounds]: https://doc.rust-lang.org/nomicon/hrtb.html
58
59pub mod convert;
60pub mod filter;
61pub mod filter_map;
62pub mod map;
63
64mod _quad;
65pub use _quad::*;
66mod _stream_error;
67pub use _stream_error::*;
68mod _triple;
69pub use _triple::*;
70
71use std::{convert::Infallible, error::Error};
72
73/// A source produces [items](Source::Item), and may also fail in the process.
74///
75/// See [module documentation](super) for the rationale of his trait.
76///
77/// # Common implementors
78///
79/// Any iterator yielding [results](std::result::Result)
80/// implements the [`Source`] trait.
81///
82/// Any iterator can also be converted to an [`Infallible`] [`Source`]
83/// thanks to the [`IntoSource`] extension trait.
84pub trait Source {
85    /// The type of items this source yields.
86    type Item<'x>;
87    /// The type of errors produced by this source.
88    type Error: Error + Send + Sync + 'static;
89
90    /// Call f for some item(s) (possibly zero) from this source, if any.
91    ///
92    /// Return `Ok(false)` if there are no more items in this source.
93    ///
94    /// Return an error if either the source or `f` errs.
95    fn try_for_some_item<E, F>(&mut self, f: F) -> StreamResult<bool, Self::Error, E>
96    where
97        E: Error + Send + Sync + 'static,
98        F: FnMut(Self::Item<'_>) -> Result<(), E>;
99
100    /// Call f for all items from this source.
101    ///
102    /// Return an error if either the source or `f` errs.
103    #[inline]
104    fn try_for_each_item<F, E>(&mut self, mut f: F) -> StreamResult<(), Self::Error, E>
105    where
106        F: FnMut(Self::Item<'_>) -> Result<(), E>,
107        E: Error + Send + Sync + 'static,
108    {
109        while self.try_for_some_item(&mut f)? {}
110        Ok(())
111    }
112
113    /// Call f for some item(s) (possibly zero) from this source, if any.
114    ///
115    /// Return false if there are no more items in this source.
116    ///
117    /// Return an error if either the source errs.
118    #[inline]
119    fn for_some_item<F>(&mut self, mut f: F) -> Result<bool, Self::Error>
120    where
121        F: FnMut(Self::Item<'_>),
122    {
123        self.try_for_some_item(|t| -> Result<(), Self::Error> {
124            f(t);
125            Ok(())
126        })
127        .map_err(StreamError::inner_into)
128    }
129
130    /// Call f for all items from this source.
131    ///
132    /// Return an error if either the source errs.
133    #[inline]
134    fn for_each_item<F>(&mut self, f: F) -> Result<(), Self::Error>
135    where
136        F: FnMut(Self::Item<'_>),
137    {
138        let mut f = f;
139        while self.for_some_item(&mut f)? {}
140        Ok(())
141    }
142
143    /// Returns a source which uses `predicate` to determine if an item should be yielded.
144    #[inline]
145    fn filter_items<F>(self, predicate: F) -> filter::FilterSource<Self, F>
146    where
147        Self: Sized,
148        F: FnMut(&Self::Item<'_>) -> bool,
149    {
150        filter::FilterSource {
151            source: self,
152            predicate,
153        }
154    }
155
156    /// Returns a source that both filters and maps.
157    ///
158    /// See also [`TripleSource::filter_triples`] and [`TripleSource::map_triples`].
159    #[inline]
160    fn filter_map_items<F, T>(self, filter_map: F) -> filter_map::FilterMapSource<Self, F>
161    where
162        Self: Sized,
163        F: FnMut(Self::Item<'_>) -> Option<T>,
164    {
165        filter_map::FilterMapSource {
166            source: self,
167            filter_map,
168        }
169    }
170
171    /// Returns a source which yield the result of `map` for each Item.
172    ///
173    /// NB: due to [some limitations in GATs (Generic) Associated Types](https://blog.rust-lang.org/2022/10/28/gats-stabilization.html),
174    /// the `map` function is currently restricted in what it can return.
175    /// In particular, passing functions as trivial as `|t| t`
176    /// currently does not compile on all implementations of [`Source`].
177    ///
178    /// As a rule of thumb,
179    /// whenever `map` returns something satisfying the `'static` lifetime,
180    /// things should work as expected.
181    #[inline]
182    fn map_items<F, T>(self, map: F) -> map::MapSource<Self, F>
183    where
184        Self: Sized,
185        F: FnMut(Self::Item<'_>) -> T,
186    {
187        map::MapSource { source: self, map }
188    }
189
190    /// Returns the bounds on the remaining length of the source.
191    ///
192    /// This method has the same contract as [`Iterator::size_hint`].
193    fn size_hint_items(&self) -> (usize, Option<usize>) {
194        (0, None)
195    }
196}
197
198impl<'a, I, T, E> Source for I
199where
200    I: Iterator<Item = Result<T, E>> + 'a,
201    E: Error + Send + Sync + 'static,
202{
203    type Item<'x> = T;
204    type Error = E;
205
206    fn try_for_some_item<E2, F>(&mut self, mut f: F) -> StreamResult<bool, Self::Error, E2>
207    where
208        E2: Error + Send + Sync + 'static,
209        F: FnMut(Self::Item<'_>) -> Result<(), E2>,
210    {
211        match self.next() {
212            Some(Err(e)) => Err(SourceError(e)),
213            Some(Ok(t)) => {
214                f(t).map_err(SinkError)?;
215                Ok(true)
216            }
217            None => Ok(false),
218        }
219    }
220
221    fn size_hint_items(&self) -> (usize, Option<usize>) {
222        self.size_hint()
223    }
224}
225
226/// An extension trait for iterators,
227/// converting them to an [`Infallible`] [`Source`].
228pub trait IntoSource: Iterator + Sized {
229    /// Convert this iterator into an [`Infallible`] [`Source`].
230    #[allow(clippy::type_complexity)]
231    fn into_source(
232        self,
233    ) -> std::iter::Map<
234        Self,
235        fn(<Self as Iterator>::Item) -> Result<<Self as Iterator>::Item, Infallible>,
236    > {
237        self.map(Ok::<_, Infallible>)
238    }
239}
240
241impl<I> IntoSource for I where I: Iterator {}