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 {}