macro_rules! mk_iterator_mod {
{
modname = $modname:ident,
itername = $itername:ident,
iteryield = $yield:ty,
extname = $extname:ident,
extfnname = $extfnname:ident,
fun = $fun:expr
} => {
pub mod $modname {
use crate::storeid::StoreId;
#[allow(unused_imports)]
use crate::store::FileLockEntry;
use crate::store::Store;
use failure::Fallible as Result;
pub struct $itername<'a>(Box<dyn Iterator<Item = Result<StoreId>> + 'a>, &'a Store);
impl<'a> $itername<'a>
{
pub fn new(inner: Box<dyn Iterator<Item = Result<StoreId>> + 'a>, store: &'a Store) -> Self {
$itername(inner, store)
}
}
impl<'a> Iterator for $itername<'a>
{
type Item = Result<$yield>;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|id| $fun(id?, self.1))
}
}
pub trait $extname<'a> {
fn $extfnname(self, store: &'a Store) -> $itername<'a>;
}
impl<'a, I> $extname<'a> for I
where I: Iterator<Item = Result<StoreId>> + 'a
{
fn $extfnname(self, store: &'a Store) -> $itername<'a> {
$itername(Box::new(self), store)
}
}
}
}
}
mk_iterator_mod! {
modname = create,
itername = StoreCreateIterator,
iteryield = FileLockEntry<'a>,
extname = StoreIdCreateIteratorExtension,
extfnname = into_create_iter,
fun = |id: StoreId, store: &'a Store| store.create(id)
}
mk_iterator_mod! {
modname = delete,
itername = StoreDeleteIterator,
iteryield = (),
extname = StoreIdDeleteIteratorExtension,
extfnname = into_delete_iter,
fun = |id: StoreId, store: &'a Store| store.delete(id)
}
mk_iterator_mod! {
modname = get,
itername = StoreGetIterator,
iteryield = Option<FileLockEntry<'a>>,
extname = StoreIdGetIteratorExtension,
extfnname = into_get_iter,
fun = |id: StoreId, store: &'a Store| store.get(id)
}
mk_iterator_mod! {
modname = retrieve,
itername = StoreRetrieveIterator,
iteryield = FileLockEntry<'a>,
extname = StoreIdRetrieveIteratorExtension,
extfnname = into_retrieve_iter,
fun = |id: StoreId, store: &'a Store| store.retrieve(id)
}
#[cfg(test)]
#[allow(dead_code)]
mod compile_test {
use crate::store::Store;
use crate::storeid::StoreId;
fn store() -> Store {
unimplemented!("Not implemented because in compile-test")
}
fn test_compile_get() {
let store = store();
let _ = store
.entries()
.unwrap()
.into_get_iter();
}
fn test_compile_get_result() {
fn to_result(e: StoreId) -> Result<StoreId, ()> {
Ok(e)
}
let store = store();
let _ = store
.entries()
.unwrap()
.into_get_iter();
}
}
use crate::storeid::StoreId;
use crate::storeid::StoreIdIterator;
use self::delete::StoreDeleteIterator;
use self::get::StoreGetIterator;
use self::retrieve::StoreRetrieveIterator;
use crate::file_abstraction::iter::PathIterator;
use crate::store::Store;
use failure::Fallible as Result;
pub struct Entries<'a>(PathIterator<'a>, &'a Store);
impl<'a> Entries<'a> {
pub(crate) fn new(pi: PathIterator<'a>, store: &'a Store) -> Self {
Entries(pi, store)
}
pub fn in_collection(self, c: &str) -> Result<Self> {
Ok(Entries(self.0.in_collection(c)?, self.1))
}
pub fn into_storeid_iter(self) -> StoreIdIterator {
use crate::storeid::StoreIdWithBase;
use crate::storeid::IntoStoreId;
let storepath = self.1.path().to_path_buf();
let iter = self.0
.into_inner()
.map(move |r| {
r.and_then(|path| {
StoreIdWithBase::from_full_path(&storepath, path)?.into_storeid()
})
});
StoreIdIterator::new(Box::new(iter))
}
pub fn into_delete_iter(self) -> StoreDeleteIterator<'a> {
StoreDeleteIterator::new(Box::new(self.0.map(|r| r.map(|id| id.without_base()))), self.1)
}
pub fn into_get_iter(self) -> StoreGetIterator<'a> {
StoreGetIterator::new(Box::new(self.0.map(|r| r.map(|id| id.without_base()))), self.1)
}
pub fn into_retrieve_iter(self) -> StoreRetrieveIterator<'a> {
StoreRetrieveIterator::new(Box::new(self.0.map(|r| r.map(|id| id.without_base()))), self.1)
}
pub fn find_by_id_substr<'b>(self, id_substr: &'b str) -> FindContains<'a, 'b> {
FindContains(self, id_substr)
}
pub fn find_by_id_startswith<'b>(self, id_substr: &'b str) -> FindStartsWith<'a, 'b> {
FindStartsWith(self, id_substr)
}
}
impl<'a> Iterator for Entries<'a> {
type Item = Result<StoreId>;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|r| r.map(|id| id.without_base()))
}
}
pub struct FindContains<'a, 'b>(Entries<'a>, &'b str);
impl<'a, 'b> Iterator for FindContains<'a, 'b> {
type Item = Result<StoreId>;
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.0.next() {
None => return None,
Some(Err(e)) => return Some(Err(e)),
Some(Ok(next)) => if next.local().to_string_lossy().contains(self.1) {
return Some(Ok(next))
},
}
}
}
}
pub struct FindStartsWith<'a, 'b>(Entries<'a>, &'b str);
impl<'a, 'b> Iterator for FindStartsWith<'a, 'b> {
type Item = Result<StoreId>;
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.0.next() {
None => return None,
Some(Err(e)) => return Some(Err(e)),
Some(Ok(next)) => if next.local().to_string_lossy().starts_with(self.1) {
return Some(Ok(next))
},
}
}
}
}
#[cfg(test)]
mod tests {
extern crate env_logger;
use std::path::PathBuf;
use std::sync::Arc;
fn setup_logging() {
let _ = env_logger::try_init();
}
use crate::store::Store;
use crate::storeid::StoreId;
use crate::file_abstraction::inmemory::InMemoryFileAbstraction;
use libimagutil::variants::generate_variants;
pub fn get_store() -> Store {
let backend = Arc::new(InMemoryFileAbstraction::default());
Store::new_with_backend(PathBuf::from("/"), &None, backend).unwrap()
}
#[test]
fn test_entries_iterator_in_collection() {
setup_logging();
let store = get_store();
let ids = {
let base = String::from("entry");
let variants = vec!["coll_1", "coll_2", "coll_3"];
let modifier = |base: &String, v: &&str| {
StoreId::new(PathBuf::from(format!("{}/{}", *v, base))).unwrap()
};
generate_variants(&base, variants.iter(), &modifier)
};
for id in ids {
let _ = store.retrieve(id).unwrap();
}
let succeeded = store.entries()
.unwrap()
.in_collection("coll_3")
.unwrap()
.map(|id| { debug!("Processing id = {:?}", id); id })
.all(|id| id.unwrap().is_in_collection(&["coll_3"]));
assert!(succeeded, "not all entries in iterator are from coll_3 collection");
}
#[test]
fn test_entries_iterator_substr() {
setup_logging();
let store = get_store();
let ids = {
let base = String::from("entry");
let variants = vec!["coll_1", "coll2", "coll_3"];
let modifier = |base: &String, v: &&str| {
StoreId::new(PathBuf::from(format!("{}/{}", *v, base))).unwrap()
};
generate_variants(&base, variants.iter(), &modifier)
};
for id in ids {
let _ = store.retrieve(id).unwrap();
}
let succeeded = store.entries()
.unwrap()
.find_by_id_substr("_")
.map(|id| { debug!("Processing id = {:?}", id); id })
.all(|id| id.unwrap().local_display_string().contains('_'));
assert!(succeeded, "not all entries in iterator contain '_'");
}
#[test]
fn test_entries_iterator_startswith() {
setup_logging();
let store = get_store();
let ids = {
let base = String::from("entry");
let variants = vec!["coll_1", "coll2", "coll_3"];
let modifier = |base: &String, v: &&str| {
StoreId::new(PathBuf::from(format!("{}/{}", *v, base))).unwrap()
};
generate_variants(&base, variants.iter(), &modifier)
};
for id in ids {
let _ = store.retrieve(id).unwrap();
}
let succeeded = store.entries()
.unwrap()
.find_by_id_startswith("entr")
.map(|id| { debug!("Processing id = {:?}", id); id })
.all(|id| id.unwrap().local_display_string().starts_with("entry"));
assert!(succeeded, "not all entries in iterator start with 'entr'");
}
}