diff --git a/01.workspace/heave/src/imp/catalog_for_each_mut.rs b/01.workspace/heave/src/imp/catalog_for_each_mut.rs index 205acef..df704ee 100644 --- a/01.workspace/heave/src/imp/catalog_for_each_mut.rs +++ b/01.workspace/heave/src/imp/catalog_for_each_mut.rs @@ -24,17 +24,25 @@ impl Catalog { F: FnMut(&mut T) -> Result<(), Box>, { self.on_items(|items| { + let mut errors: Vec> = 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; } } - Ok(()) + if errors.is_empty() { + Ok(()) + } else { + Err(FailedTo::ExecutePredicate(errors)) + } }) } } diff --git a/01.workspace/heave/src/str/failed_to.rs b/01.workspace/heave/src/str/failed_to.rs index 607939a..34f995a 100644 --- a/01.workspace/heave/src/str/failed_to.rs +++ b/01.workspace/heave/src/str/failed_to.rs @@ -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), + ExecutePredicate(Vec>), /// Failed to initialize the database. InitDatabase, /// Failed to load data from the database. diff --git a/01.workspace/heave/src/tst/catalog_for_each_mut.rs b/01.workspace/heave/src/tst/catalog_for_each_mut.rs index f6afe0c..962b66f 100644 --- a/01.workspace/heave/src/tst/catalog_for_each_mut.rs +++ b/01.workspace/heave/src/tst/catalog_for_each_mut.rs @@ -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::(), + 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(()) + }); + } }