feat: return a vector of boxed error from catalog.for_each_mut
execution is not interrupted and user can raise any error implementing Error trait
This commit is contained in:
@@ -24,17 +24,25 @@ impl Catalog {
|
||||
F: FnMut(&mut T) -> Result<(), Box<dyn error::Error>>,
|
||||
{
|
||||
self.on_items(|items| {
|
||||
let mut errors: Vec<Box<dyn error::Error>> = Vec::new();
|
||||
for entity in items.values_mut() {
|
||||
let original_item =
|
||||
T::try_from(entity.clone()).map_err(|_| FailedTo::ConvertEntity)?;
|
||||
let mut item = T::try_from(entity.clone()).map_err(|_| FailedTo::ConvertEntity)?;
|
||||
predicate(&mut item).map_err(FailedTo::ExecutePredicate)?;
|
||||
let mut item = original_item.clone();
|
||||
let result = predicate(&mut item);
|
||||
if let Err(e) = result {
|
||||
errors.push(e);
|
||||
}
|
||||
if item != original_item {
|
||||
*entity = T::try_into(item).map_err(|_| FailedTo::ConvertObject)?;
|
||||
entity.state = EntityState::Updated;
|
||||
}
|
||||
}
|
||||
if errors.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(FailedTo::ExecutePredicate(errors))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ pub enum FailedTo {
|
||||
/// Failed to convert from Value to type.
|
||||
ConvertValue,
|
||||
/// Failed to execute predicate to mutate an item.
|
||||
ExecutePredicate(Box<dyn error::Error>),
|
||||
ExecutePredicate(Vec<Box<dyn error::Error>>),
|
||||
/// Failed to initialize the database.
|
||||
InitDatabase,
|
||||
/// Failed to load data from the database.
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::*;
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum Test {
|
||||
Error(String),
|
||||
}
|
||||
impl std::fmt::Display for Test {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
||||
write!(f, "error!")
|
||||
}
|
||||
}
|
||||
impl std::error::Error for Test {}
|
||||
fn prepare_catalog(catalog: &mut Catalog) {
|
||||
let items = vec![
|
||||
Item {
|
||||
@@ -51,4 +61,53 @@ mod tests {
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
#[test]
|
||||
fn for_each_mut_should_handle_empty_catalog() {
|
||||
let catalog = Catalog::new("test.db");
|
||||
let result = catalog.for_each_mut(|_item: &mut Item| Ok(()));
|
||||
assert!(result.is_ok());
|
||||
let count = catalog.with_items(|items| Ok(items.len())).unwrap();
|
||||
assert_eq!(count, 0);
|
||||
}
|
||||
#[test]
|
||||
fn for_each_mut_should_handle_closure_error() {
|
||||
let mut catalog = Catalog::new("test.db");
|
||||
prepare_catalog(&mut catalog);
|
||||
let result = catalog.for_each_mut(|item: &mut Item| {
|
||||
if item.id == "2" {
|
||||
Err(Box::new(Test::Error(item.id.clone())))
|
||||
} else {
|
||||
item.price += 50;
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
assert!(result.is_err());
|
||||
if let Err(FailedTo::ExecutePredicate(e)) = result {
|
||||
assert_eq!(
|
||||
e[0].downcast_ref::<Test>(),
|
||||
Some(&Test::Error("2".to_string()))
|
||||
);
|
||||
}
|
||||
let item1: Item = catalog.get("1").unwrap().unwrap();
|
||||
let item2: Item = catalog.get("2").unwrap().unwrap();
|
||||
let item3: Item = catalog.get("3").unwrap().unwrap();
|
||||
assert_eq!(item1.price, 150); // Modified
|
||||
assert_eq!(item2.price, 200); // Not modified due to error
|
||||
assert_eq!(item3.price, 350); // Modified
|
||||
let _ = catalog.with_items(|items| {
|
||||
let entity = items.get("1").unwrap();
|
||||
assert_eq!(entity.state, EntityState::Updated);
|
||||
Ok(())
|
||||
});
|
||||
let _ = catalog.with_items(|items| {
|
||||
let entity = items.get("2").unwrap();
|
||||
assert_eq!(entity.state, EntityState::New); // Should remain Unchanged
|
||||
Ok(())
|
||||
});
|
||||
let _ = catalog.with_items(|items| {
|
||||
let entity = items.get("3").unwrap();
|
||||
assert_eq!(entity.state, EntityState::Updated);
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user