sophia_api/source/
filter_map.rs

1//! I define [`FilterMapSource`] the result type of [`Source::filter_map_items`].
2use super::*;
3use std::{collections::VecDeque, error::Error};
4
5/// The result of [`Source::filter_map_items`].
6pub struct FilterMapSource<S, F> {
7    pub(in super::super) source: S,
8    pub(in super::super) filter_map: F,
9}
10
11impl<S, F, T> Source for FilterMapSource<S, F>
12where
13    S: Source,
14    F: FnMut(S::Item<'_>) -> Option<T>,
15{
16    type Item<'x> = T;
17    type Error = S::Error;
18
19    fn try_for_some_item<E, F2>(&mut self, mut f: F2) -> StreamResult<bool, Self::Error, E>
20    where
21        E: Error + Send + Sync + 'static,
22        F2: FnMut(Self::Item<'_>) -> Result<(), E>,
23    {
24        let filter_map = &mut self.filter_map;
25        self.source.try_for_some_item(|t| match (filter_map)(t) {
26            None => Ok(()),
27            Some(out) => f(out),
28        })
29    }
30
31    fn size_hint_items(&self) -> (usize, Option<usize>) {
32        (0, self.source.size_hint_items().1)
33    }
34}
35
36impl<S, F, T> IntoIterator for FilterMapSource<S, F>
37where
38    S: Source,
39    F: FnMut(S::Item<'_>) -> Option<T>,
40{
41    type Item = Result<T, S::Error>;
42    type IntoIter = FilterMapSourceIterator<S, F, T, S::Error>;
43
44    fn into_iter(self) -> Self::IntoIter {
45        FilterMapSourceIterator {
46            source: self.source,
47            filter_map: self.filter_map,
48            buffer: VecDeque::new(),
49        }
50    }
51}
52
53/// [`Iterator`] implementation for the returned value of [`Source::filter_map_items`].
54pub struct FilterMapSourceIterator<S, F, T, E> {
55    source: S,
56    filter_map: F,
57    buffer: VecDeque<Result<T, E>>,
58}
59
60impl<S, F, T> Iterator for FilterMapSourceIterator<S, F, T, S::Error>
61where
62    S: Source,
63    F: FnMut(S::Item<'_>) -> Option<T>,
64{
65    type Item = Result<T, S::Error>;
66    fn next(&mut self) -> Option<Result<T, S::Error>> {
67        let mut remaining = true;
68        let mut buffer = VecDeque::new();
69        std::mem::swap(&mut self.buffer, &mut buffer);
70        while buffer.is_empty() && remaining {
71            let resb = self.source.for_some_item(|i| {
72                if let Some(t) = (self.filter_map)(i) {
73                    buffer.push_back(Ok(t));
74                }
75            });
76            match resb {
77                Ok(b) => {
78                    remaining = b;
79                }
80                Err(err) => {
81                    buffer.push_back(Err(err));
82                    remaining = false;
83                }
84            }
85        }
86        std::mem::swap(&mut self.buffer, &mut buffer);
87        self.buffer.pop_front()
88    }
89
90    fn size_hint(&self) -> (usize, Option<usize>) {
91        self.source.size_hint_items()
92    }
93}
94
95#[cfg(test)]
96mod test {
97    use super::*;
98    use crate::dataset::{Dataset, MutableDataset};
99    use crate::graph::{Graph, MutableGraph};
100    use crate::quad::{Quad, Spog};
101    use crate::term::ez_term;
102    use crate::term::{SimpleTerm, Term};
103    use crate::triple::Triple;
104
105    // check that the result of TripleSource::filter_map_triples implements the expected traits,
106    // and that they work as expected
107
108    #[test]
109    fn ts_filter_map_to_triples() {
110        let g = vec![
111            [ez_term(":a"), ez_term(":b"), ez_term(":c")],
112            [ez_term(":d"), ez_term(":e"), ez_term(":f")],
113            [ez_term(":g"), ez_term(":h"), ez_term(":i")],
114        ];
115        let mut h: Vec<[SimpleTerm; 3]> = vec![];
116        g.triples()
117            .filter_map_triples(|t| {
118                (!Term::eq(t.p(), ez_term(":e"))).then_some([t.o(), t.p(), t.s()])
119            })
120            .for_each_triple(|t| {
121                h.insert_triple(t).unwrap();
122            })
123            .unwrap();
124        assert_eq!(
125            h,
126            vec![
127                [ez_term(":c"), ez_term(":b"), ez_term(":a")],
128                [ez_term(":i"), ez_term(":h"), ez_term(":g")],
129            ]
130        )
131    }
132
133    #[test]
134    fn ts_filter_map_to_quads() {
135        let g = vec![
136            [ez_term(":a"), ez_term(":b"), ez_term(":c")],
137            [ez_term(":g"), ez_term(":h"), ez_term(":i")],
138        ];
139        let mut h: Vec<Spog<SimpleTerm>> = vec![];
140        g.triples()
141            .filter_map_triples(|t| {
142                (!Term::eq(t.p(), ez_term(":e"))).then_some(([t.o(), t.p(), t.s()], None))
143            })
144            .for_each_quad(|q| {
145                h.insert_quad(q).unwrap();
146            })
147            .unwrap();
148        assert_eq!(
149            h,
150            vec![
151                ([ez_term(":c"), ez_term(":b"), ez_term(":a")], None),
152                ([ez_term(":i"), ez_term(":h"), ez_term(":g")], None),
153            ]
154        )
155    }
156
157    #[test]
158    fn ts_filter_map_iter() {
159        let g = vec![
160            [ez_term(":a"), ez_term(":b"), ez_term(":c")],
161            [ez_term(":d"), ez_term(":e"), ez_term(":f")],
162            [ez_term(":g"), ez_term(":h"), ez_term(":i")],
163        ];
164        let h: Result<Vec<String>, _> = g
165            .triples()
166            .filter_map_triples(|t| {
167                (!Term::eq(t.p(), ez_term(":e"))).then_some(t.s().iri().unwrap().to_string())
168            })
169            .into_iter()
170            .collect();
171        assert_eq!(h.unwrap(), vec!["tag:a".to_string(), "tag:g".to_string(),])
172    }
173
174    // check that the result of QuadSource::filter_map_quads implements the expected traits
175    // and that they work as expected
176
177    #[test]
178    fn qs_filter_map_to_triples() {
179        let d = vec![
180            ([ez_term(":a"), ez_term(":b"), ez_term(":c")], None),
181            ([ez_term(":d"), ez_term(":e"), ez_term(":f")], None),
182            ([ez_term(":g"), ez_term(":h"), ez_term(":i")], None),
183        ];
184        let mut h: Vec<[SimpleTerm; 3]> = vec![];
185        d.quads()
186            .filter_map_quads(|q| {
187                (!Term::eq(q.p(), ez_term(":e"))).then_some([q.o(), q.p(), q.s()])
188            })
189            .for_each_triple(|t| {
190                h.insert_triple(t).unwrap();
191            })
192            .unwrap();
193        assert_eq!(
194            h,
195            vec![
196                [ez_term(":c"), ez_term(":b"), ez_term(":a")],
197                [ez_term(":i"), ez_term(":h"), ez_term(":g")],
198            ]
199        )
200    }
201
202    #[test]
203    fn qs_filter_map_to_quads() {
204        let d = vec![
205            ([ez_term(":a"), ez_term(":b"), ez_term(":c")], None),
206            ([ez_term(":d"), ez_term(":e"), ez_term(":f")], None),
207            ([ez_term(":g"), ez_term(":h"), ez_term(":i")], None),
208        ];
209        let mut h: Vec<Spog<SimpleTerm>> = vec![];
210        d.quads()
211            .filter_map_quads(|q| {
212                (!Term::eq(q.p(), ez_term(":e"))).then_some(([q.o(), q.p(), q.s()], q.g()))
213            })
214            .for_each_quad(|q| {
215                h.insert_quad(q).unwrap();
216            })
217            .unwrap();
218        assert_eq!(
219            h,
220            vec![
221                ([ez_term(":c"), ez_term(":b"), ez_term(":a")], None),
222                ([ez_term(":i"), ez_term(":h"), ez_term(":g")], None),
223            ]
224        )
225    }
226
227    #[test]
228    fn qs_filter_map_iter() {
229        let d = vec![
230            ([ez_term(":a"), ez_term(":b"), ez_term(":c")], None),
231            ([ez_term(":d"), ez_term(":e"), ez_term(":f")], None),
232            ([ez_term(":g"), ez_term(":h"), ez_term(":i")], None),
233        ];
234        let h: Result<Vec<String>, _> = d
235            .quads()
236            .filter_map_quads(|q| {
237                (!Term::eq(q.p(), ez_term(":e"))).then_some(q.s().iri().unwrap().to_string())
238            })
239            .into_iter()
240            .collect();
241        assert_eq!(h.unwrap(), vec!["tag:a".to_string(), "tag:g".to_string(),])
242    }
243}