1macro_rules! mk_iterator_mod {
21 {
22 modname = $modname:ident,
23 itername = $itername:ident,
24 iteryield = $yield:ty,
25 extname = $extname:ident,
26 extfnname = $extfnname:ident,
27 fun = $fun:expr
28 } => {
29 pub mod $modname {
30 use crate::storeid::StoreId;
31 #[allow(unused_imports)]
32 use crate::store::FileLockEntry;
33 use crate::store::Store;
34 use failure::Fallible as Result;
35
36 pub struct $itername<'a>(Box<dyn Iterator<Item = Result<StoreId>> + 'a>, &'a Store);
37
38 impl<'a> $itername<'a>
39 {
40 pub fn new(inner: Box<dyn Iterator<Item = Result<StoreId>> + 'a>, store: &'a Store) -> Self {
41 $itername(inner, store)
42 }
43 }
44
45 impl<'a> Iterator for $itername<'a>
46 {
47 type Item = Result<$yield>;
48
49 fn next(&mut self) -> Option<Self::Item> {
50 self.0.next().map(|id| $fun(id?, self.1))
51 }
52 }
53
54 pub trait $extname<'a> {
55 fn $extfnname(self, store: &'a Store) -> $itername<'a>;
56 }
57
58 impl<'a, I> $extname<'a> for I
59 where I: Iterator<Item = Result<StoreId>> + 'a
60 {
61 fn $extfnname(self, store: &'a Store) -> $itername<'a> {
62 $itername(Box::new(self), store)
63 }
64 }
65 }
66 }
67}
68
69mk_iterator_mod! {
70 modname = create,
71 itername = StoreCreateIterator,
72 iteryield = FileLockEntry<'a>,
73 extname = StoreIdCreateIteratorExtension,
74 extfnname = into_create_iter,
75 fun = |id: StoreId, store: &'a Store| store.create(id)
76}
77
78mk_iterator_mod! {
79 modname = delete,
80 itername = StoreDeleteIterator,
81 iteryield = (),
82 extname = StoreIdDeleteIteratorExtension,
83 extfnname = into_delete_iter,
84 fun = |id: StoreId, store: &'a Store| store.delete(id)
85}
86
87mk_iterator_mod! {
88 modname = get,
89 itername = StoreGetIterator,
90 iteryield = Option<FileLockEntry<'a>>,
91 extname = StoreIdGetIteratorExtension,
92 extfnname = into_get_iter,
93 fun = |id: StoreId, store: &'a Store| store.get(id)
94}
95
96mk_iterator_mod! {
97 modname = retrieve,
98 itername = StoreRetrieveIterator,
99 iteryield = FileLockEntry<'a>,
100 extname = StoreIdRetrieveIteratorExtension,
101 extfnname = into_retrieve_iter,
102 fun = |id: StoreId, store: &'a Store| store.retrieve(id)
103}
104
105#[cfg(test)]
106#[allow(dead_code)]
107mod compile_test {
108
109 use crate::store::Store;
113 use crate::storeid::StoreId;
114
115 fn store() -> Store {
116 unimplemented!("Not implemented because in compile-test")
117 }
118
119 fn test_compile_get() {
120 let store = store();
121 let _ = store
122 .entries()
123 .unwrap()
124 .into_get_iter();
125 }
126
127 fn test_compile_get_result() {
128 fn to_result(e: StoreId) -> Result<StoreId, ()> {
129 Ok(e)
130 }
131
132 let store = store();
133 let _ = store
134 .entries()
135 .unwrap()
136 .into_get_iter();
137 }
138}
139
140use crate::storeid::StoreId;
141use crate::storeid::StoreIdIterator;
142use self::delete::StoreDeleteIterator;
143use self::get::StoreGetIterator;
144use self::retrieve::StoreRetrieveIterator;
145use crate::file_abstraction::iter::PathIterator;
146use crate::store::Store;
147use failure::Fallible as Result;
148
149pub struct Entries<'a>(PathIterator<'a>, &'a Store);
171
172impl<'a> Entries<'a> {
173
174 pub(crate) fn new(pi: PathIterator<'a>, store: &'a Store) -> Self {
175 Entries(pi, store)
176 }
177
178 pub fn in_collection(self, c: &str) -> Result<Self> {
179 Ok(Entries(self.0.in_collection(c)?, self.1))
180 }
181
182 pub fn into_storeid_iter(self) -> StoreIdIterator {
189 use crate::storeid::StoreIdWithBase;
190 use crate::storeid::IntoStoreId;
191
192 let storepath = self.1.path().to_path_buf();
193
194 let iter = self.0
195 .into_inner()
196 .map(move |r| {
197 r.and_then(|path| {
198 StoreIdWithBase::from_full_path(&storepath, path)?.into_storeid()
199 })
200 });
201 StoreIdIterator::new(Box::new(iter))
202 }
203
204 pub fn into_delete_iter(self) -> StoreDeleteIterator<'a> {
208 StoreDeleteIterator::new(Box::new(self.0.map(|r| r.map(|id| id.without_base()))), self.1)
209 }
210
211 pub fn into_get_iter(self) -> StoreGetIterator<'a> {
215 StoreGetIterator::new(Box::new(self.0.map(|r| r.map(|id| id.without_base()))), self.1)
216 }
217
218 pub fn into_retrieve_iter(self) -> StoreRetrieveIterator<'a> {
222 StoreRetrieveIterator::new(Box::new(self.0.map(|r| r.map(|id| id.without_base()))), self.1)
223 }
224
225 pub fn find_by_id_substr<'b>(self, id_substr: &'b str) -> FindContains<'a, 'b> {
239 FindContains(self, id_substr)
240 }
241
242 pub fn find_by_id_startswith<'b>(self, id_substr: &'b str) -> FindStartsWith<'a, 'b> {
247 FindStartsWith(self, id_substr)
248 }
249
250}
251
252impl<'a> Iterator for Entries<'a> {
253 type Item = Result<StoreId>;
254
255 fn next(&mut self) -> Option<Self::Item> {
256 self.0.next().map(|r| r.map(|id| id.without_base()))
257 }
258}
259
260pub struct FindContains<'a, 'b>(Entries<'a>, &'b str);
261
262impl<'a, 'b> Iterator for FindContains<'a, 'b> {
263 type Item = Result<StoreId>;
264
265 fn next(&mut self) -> Option<Self::Item> {
266 loop {
267 match self.0.next() {
268 None => return None,
269 Some(Err(e)) => return Some(Err(e)),
270 Some(Ok(next)) => if next.local().to_string_lossy().contains(self.1) {
271 return Some(Ok(next))
272 }, }
274 }
275 }
276}
277
278pub struct FindStartsWith<'a, 'b>(Entries<'a>, &'b str);
279
280impl<'a, 'b> Iterator for FindStartsWith<'a, 'b> {
281 type Item = Result<StoreId>;
282
283 fn next(&mut self) -> Option<Self::Item> {
284 loop {
285 match self.0.next() {
286 None => return None,
287 Some(Err(e)) => return Some(Err(e)),
288 Some(Ok(next)) => if next.local().to_string_lossy().starts_with(self.1) {
289 return Some(Ok(next))
290 }, }
292 }
293 }
294}
295
296#[cfg(test)]
297mod tests {
298 extern crate env_logger;
299
300 use std::path::PathBuf;
301 use std::sync::Arc;
302
303 fn setup_logging() {
304 let _ = env_logger::try_init();
305 }
306
307 use crate::store::Store;
308 use crate::storeid::StoreId;
309 use crate::file_abstraction::inmemory::InMemoryFileAbstraction;
310 use libimagutil::variants::generate_variants;
311
312 pub fn get_store() -> Store {
313 let backend = Arc::new(InMemoryFileAbstraction::default());
314 Store::new_with_backend(PathBuf::from("/"), &None, backend).unwrap()
315 }
316
317 #[test]
318 fn test_entries_iterator_in_collection() {
319 setup_logging();
320 let store = get_store();
321
322 let ids = {
323 let base = String::from("entry");
324 let variants = vec!["coll_1", "coll_2", "coll_3"];
325 let modifier = |base: &String, v: &&str| {
326 StoreId::new(PathBuf::from(format!("{}/{}", *v, base))).unwrap()
327 };
328
329 generate_variants(&base, variants.iter(), &modifier)
330 };
331
332 for id in ids {
333 let _ = store.retrieve(id).unwrap();
334 }
335
336 let succeeded = store.entries()
337 .unwrap()
338 .in_collection("coll_3")
339 .unwrap()
340 .map(|id| { debug!("Processing id = {:?}", id); id })
341 .all(|id| id.unwrap().is_in_collection(&["coll_3"]));
342
343 assert!(succeeded, "not all entries in iterator are from coll_3 collection");
344 }
345
346 #[test]
347 fn test_entries_iterator_substr() {
348 setup_logging();
349 let store = get_store();
350
351 let ids = {
352 let base = String::from("entry");
353 let variants = vec!["coll_1", "coll2", "coll_3"];
354 let modifier = |base: &String, v: &&str| {
355 StoreId::new(PathBuf::from(format!("{}/{}", *v, base))).unwrap()
356 };
357
358 generate_variants(&base, variants.iter(), &modifier)
359 };
360
361 for id in ids {
362 let _ = store.retrieve(id).unwrap();
363 }
364
365 let succeeded = store.entries()
366 .unwrap()
367 .find_by_id_substr("_")
368 .map(|id| { debug!("Processing id = {:?}", id); id })
369 .all(|id| id.unwrap().local_display_string().contains('_'));
370
371 assert!(succeeded, "not all entries in iterator contain '_'");
372 }
373
374 #[test]
375 fn test_entries_iterator_startswith() {
376 setup_logging();
377 let store = get_store();
378
379 let ids = {
380 let base = String::from("entry");
381 let variants = vec!["coll_1", "coll2", "coll_3"];
382 let modifier = |base: &String, v: &&str| {
383 StoreId::new(PathBuf::from(format!("{}/{}", *v, base))).unwrap()
384 };
385
386 generate_variants(&base, variants.iter(), &modifier)
387 };
388
389 for id in ids {
390 let _ = store.retrieve(id).unwrap();
391 }
392
393 let succeeded = store.entries()
394 .unwrap()
395 .find_by_id_startswith("entr")
396 .map(|id| { debug!("Processing id = {:?}", id); id })
397 .all(|id| id.unwrap().local_display_string().starts_with("entry"));
398
399 assert!(succeeded, "not all entries in iterator start with 'entr'");
400 }
401
402}
403