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>>,
|
F: FnMut(&mut T) -> Result<(), Box<dyn error::Error>>,
|
||||||
{
|
{
|
||||||
self.on_items(|items| {
|
self.on_items(|items| {
|
||||||
|
let mut errors: Vec<Box<dyn error::Error>> = Vec::new();
|
||||||
for entity in items.values_mut() {
|
for entity in items.values_mut() {
|
||||||
let original_item =
|
let original_item =
|
||||||
T::try_from(entity.clone()).map_err(|_| FailedTo::ConvertEntity)?;
|
T::try_from(entity.clone()).map_err(|_| FailedTo::ConvertEntity)?;
|
||||||
let mut item = T::try_from(entity.clone()).map_err(|_| FailedTo::ConvertEntity)?;
|
let mut item = original_item.clone();
|
||||||
predicate(&mut item).map_err(FailedTo::ExecutePredicate)?;
|
let result = predicate(&mut item);
|
||||||
|
if let Err(e) = result {
|
||||||
|
errors.push(e);
|
||||||
|
}
|
||||||
if item != original_item {
|
if item != original_item {
|
||||||
*entity = T::try_into(item).map_err(|_| FailedTo::ConvertObject)?;
|
*entity = T::try_into(item).map_err(|_| FailedTo::ConvertObject)?;
|
||||||
entity.state = EntityState::Updated;
|
entity.state = EntityState::Updated;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
if errors.is_empty() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(FailedTo::ExecutePredicate(errors))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ pub enum FailedTo {
|
|||||||
/// Failed to convert from Value to type.
|
/// Failed to convert from Value to type.
|
||||||
ConvertValue,
|
ConvertValue,
|
||||||
/// Failed to execute predicate to mutate an item.
|
/// Failed to execute predicate to mutate an item.
|
||||||
ExecutePredicate(Box<dyn error::Error>),
|
ExecutePredicate(Vec<Box<dyn error::Error>>),
|
||||||
/// Failed to initialize the database.
|
/// Failed to initialize the database.
|
||||||
InitDatabase,
|
InitDatabase,
|
||||||
/// Failed to load data from the database.
|
/// Failed to load data from the database.
|
||||||
|
|||||||
@@ -1,6 +1,16 @@
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::*;
|
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) {
|
fn prepare_catalog(catalog: &mut Catalog) {
|
||||||
let items = vec![
|
let items = vec![
|
||||||
Item {
|
Item {
|
||||||
@@ -51,4 +61,53 @@ mod tests {
|
|||||||
Ok(())
|
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