Reimplement hook registration and execution

This commit is contained in:
Matthias Beyer 2016-03-04 20:33:08 +01:00
parent d6a581e69f
commit 679865464b

View file

@ -23,11 +23,15 @@ use error::{StoreError, StoreErrorKind};
use storeid::{StoreId, StoreIdIterator}; use storeid::{StoreId, StoreIdIterator};
use lazyfile::LazyFile; use lazyfile::LazyFile;
use hook::aspect::Aspect;
use hook::result::HookResult; use hook::result::HookResult;
use hook::accessor::{ MutableHookDataAccessor, use hook::accessor::{ MutableHookDataAccessor,
NonMutableHookDataAccessor, NonMutableHookDataAccessor,
StoreIdAccessor,
HookDataAccessor, HookDataAccessor,
HookDataAccessorProvider}; HookDataAccessorProvider};
use hook::position::HookPosition;
use hook::Hook;
/// The Result Type returned by any interaction with the store that could fail /// The Result Type returned by any interaction with the store that could fail
pub type Result<T> = RResult<T, StoreError>; pub type Result<T> = RResult<T, StoreError>;
@ -105,16 +109,16 @@ pub struct Store {
* Registered hooks * Registered hooks
*/ */
pre_read_hooks : Arc<Mutex<Vec<Box<PreReadHook>>>>, pre_read_aspects : Arc<Mutex<Vec<Aspect>>>,
post_read_hooks : Arc<Mutex<Vec<Box<PostReadHook>>>>, post_read_aspects : Arc<Mutex<Vec<Aspect>>>,
pre_create_hooks : Arc<Mutex<Vec<Box<PreCreateHook>>>>, pre_create_aspects : Arc<Mutex<Vec<Aspect>>>,
post_create_hooks : Arc<Mutex<Vec<Box<PostCreateHook>>>>, post_create_aspects : Arc<Mutex<Vec<Aspect>>>,
pre_retrieve_hooks : Arc<Mutex<Vec<Box<PreRetrieveHook>>>>, pre_retrieve_aspects : Arc<Mutex<Vec<Aspect>>>,
post_retrieve_hooks : Arc<Mutex<Vec<Box<PostRetrieveHook>>>>, post_retrieve_aspects : Arc<Mutex<Vec<Aspect>>>,
pre_update_hooks : Arc<Mutex<Vec<Box<PreUpdateHook>>>>, pre_update_aspects : Arc<Mutex<Vec<Aspect>>>,
post_update_hooks : Arc<Mutex<Vec<Box<PostUpdateHook>>>>, post_update_aspects : Arc<Mutex<Vec<Aspect>>>,
pre_delete_hooks : Arc<Mutex<Vec<Box<PreDeleteHook>>>>, pre_delete_aspects : Arc<Mutex<Vec<Aspect>>>,
post_delete_hooks : Arc<Mutex<Vec<Box<PostDeleteHook>>>>, post_delete_aspects : Arc<Mutex<Vec<Aspect>>>,
/** /**
* Internal Path->File cache map * Internal Path->File cache map
@ -151,16 +155,16 @@ impl Store {
debug!("Store building succeeded"); debug!("Store building succeeded");
Ok(Store { Ok(Store {
location: location, location: location,
pre_read_hooks : Arc::new(Mutex::new(vec![])), pre_read_aspects : Arc::new(Mutex::new(vec![])),
post_read_hooks : Arc::new(Mutex::new(vec![])), post_read_aspects : Arc::new(Mutex::new(vec![])),
pre_create_hooks : Arc::new(Mutex::new(vec![])), pre_create_aspects : Arc::new(Mutex::new(vec![])),
post_create_hooks : Arc::new(Mutex::new(vec![])), post_create_aspects : Arc::new(Mutex::new(vec![])),
pre_retrieve_hooks : Arc::new(Mutex::new(vec![])), pre_retrieve_aspects : Arc::new(Mutex::new(vec![])),
post_retrieve_hooks : Arc::new(Mutex::new(vec![])), post_retrieve_aspects : Arc::new(Mutex::new(vec![])),
pre_update_hooks : Arc::new(Mutex::new(vec![])), pre_update_aspects : Arc::new(Mutex::new(vec![])),
post_update_hooks : Arc::new(Mutex::new(vec![])), post_update_aspects : Arc::new(Mutex::new(vec![])),
pre_delete_hooks : Arc::new(Mutex::new(vec![])), pre_delete_aspects : Arc::new(Mutex::new(vec![])),
post_delete_hooks : Arc::new(Mutex::new(vec![])), post_delete_aspects : Arc::new(Mutex::new(vec![])),
entries: Arc::new(RwLock::new(HashMap::new())), entries: Arc::new(RwLock::new(HashMap::new())),
}) })
} }
@ -176,7 +180,7 @@ impl Store {
/// Creates the Entry at the given location (inside the entry) /// Creates the Entry at the given location (inside the entry)
pub fn create<'a>(&'a self, id: StoreId) -> Result<FileLockEntry<'a>> { pub fn create<'a>(&'a self, id: StoreId) -> Result<FileLockEntry<'a>> {
let id = self.storify_id(id); let id = self.storify_id(id);
if let Err(e) = self.execute_pre_create_hooks(&id) { if let Err(e) = self.execute_hooks_for_id(self.pre_create_aspects.clone(), &id) {
return Err(e); return Err(e);
} }
@ -194,14 +198,17 @@ impl Store {
se se
}); });
self.execute_post_create_hooks(FileLockEntry::new(self, Entry::new(id.clone()), id)) let mut fle = FileLockEntry::new(self, Entry::new(id.clone()), id);
self.execute_hooks_for_mut_file(self.post_create_aspects.clone(), &mut fle)
.map_err(|e| StoreError::new(StoreErrorKind::PostHookExecuteError, Some(Box::new(e))))
.map(|_| fle)
} }
/// Borrow a given Entry. When the `FileLockEntry` is either `update`d or /// Borrow a given Entry. When the `FileLockEntry` is either `update`d or
/// dropped, the new Entry is written to disk /// dropped, the new Entry is written to disk
pub fn retrieve<'a>(&'a self, id: StoreId) -> Result<FileLockEntry<'a>> { pub fn retrieve<'a>(&'a self, id: StoreId) -> Result<FileLockEntry<'a>> {
let id = self.storify_id(id); let id = self.storify_id(id);
if let Err(e) = self.execute_pre_retrieve_hooks(&id) { if let Err(e) = self.execute_hooks_for_id(self.pre_retrieve_aspects.clone(), &id) {
return Err(e); return Err(e);
} }
@ -214,7 +221,15 @@ impl Store {
se.status = StoreEntryStatus::Borrowed; se.status = StoreEntryStatus::Borrowed;
entry entry
}) })
.and_then(|e| self.execute_post_retrieve_hooks(FileLockEntry::new(self, e, id))) .map(|e| FileLockEntry::new(self, e, id))
.and_then(|mut fle| {
if let Err(e) = self.execute_hooks_for_mut_file(self.pre_retrieve_aspects.clone(), &mut fle) {
Err(StoreError::new(StoreErrorKind::HookExecutionError, Some(Box::new(e))))
} else {
Ok(fle)
}
})
} }
/// Iterate over all StoreIds for one module name /// Iterate over all StoreIds for one module name
@ -224,7 +239,7 @@ impl Store {
/// Return the `FileLockEntry` and write to disk /// Return the `FileLockEntry` and write to disk
pub fn update<'a>(&'a self, mut entry: FileLockEntry<'a>) -> Result<()> { pub fn update<'a>(&'a self, mut entry: FileLockEntry<'a>) -> Result<()> {
if let Err(e) = self.execute_pre_update_hooks(&mut entry) { if let Err(e) = self.execute_hooks_for_mut_file(self.pre_update_aspects.clone(), &mut entry) {
return Err(e); return Err(e);
} }
@ -232,8 +247,7 @@ impl Store {
return Err(e); return Err(e);
} }
self.execute_post_update_hooks(entry) self.execute_hooks_for_mut_file(self.post_update_aspects.clone(), &mut entry)
.map(|_| ())
} }
/// Internal method to write to the filesystem store. /// Internal method to write to the filesystem store.
@ -284,7 +298,7 @@ impl Store {
/// Delete an entry /// Delete an entry
pub fn delete(&self, id: StoreId) -> Result<()> { pub fn delete(&self, id: StoreId) -> Result<()> {
let id = self.storify_id(id); let id = self.storify_id(id);
if let Err(e) = self.execute_pre_delete_hooks(&id) { if let Err(e) = self.execute_hooks_for_id(self.pre_delete_aspects.clone(), &id) {
return Err(e); return Err(e);
} }
@ -306,7 +320,7 @@ impl Store {
return Err(StoreError::new(StoreErrorKind::FileError, Some(Box::new(e)))); return Err(StoreError::new(StoreErrorKind::FileError, Some(Box::new(e))));
} }
self.execute_post_delete_hooks(&id) self.execute_hooks_for_id(self.post_delete_aspects.clone(), &id)
} }
/// Gets the path where this store is on the disk /// Gets the path where this store is on the disk
@ -314,327 +328,96 @@ impl Store {
&self.location &self.location
} }
pub fn register_pre_read_hook(&self, h: Box<PreReadHook>) -> Result<()> { pub fn register_hook(&mut self,
debug!("Registering pre-read hook: {:?}", h); position: HookPosition,
self.pre_read_hooks aspect_name: &String,
.deref() h: Box<Hook>)
.lock() -> Result<()>
.map_err(|_| StoreError::new(StoreErrorKind::HookRegisterError, None))
// TODO: cause: Some(Box::new(e))
.map(|mut guard| guard.deref_mut().push(h))
}
pub fn register_post_read_hook(&self, h: Box<PostReadHook>) -> Result<()> {
debug!("Registering post-read hook: {:?}", h);
self.post_read_hooks
.deref()
.lock()
.map_err(|_| StoreError::new(StoreErrorKind::HookRegisterError, None))
// TODO: cause: Some(Box::new(e))
.map(|mut guard| guard.deref_mut().push(h))
}
pub fn register_pre_create_hook(&self, h: Box<PreCreateHook>) -> Result<()> {
debug!("Registering pre-create hook: {:?}", h);
self.pre_create_hooks
.deref()
.lock()
.map_err(|_| StoreError::new(StoreErrorKind::HookRegisterError, None))
// TODO: cause: Some(Box::new(e))
.map(|mut guard| guard.deref_mut().push(h))
}
pub fn register_post_create_hook(&self, h: Box<PostCreateHook>) -> Result<()> {
debug!("Registering post-create hook: {:?}", h);
self.post_create_hooks
.deref()
.lock()
.map_err(|_| StoreError::new(StoreErrorKind::HookRegisterError, None))
// TODO: cause: Some(Box::new(e))
.map(|mut guard| guard.deref_mut().push(h))
}
pub fn register_pre_retrieve_hook(&self, h: Box<PreRetrieveHook>) -> Result<()> {
debug!("Registering pre-retrieve hook: {:?}", h);
self.pre_retrieve_hooks
.deref()
.lock()
.map_err(|_| StoreError::new(StoreErrorKind::HookRegisterError, None))
// TODO: cause: Some(Box::new(e))
.map(|mut guard| guard.deref_mut().push(h))
}
pub fn register_post_retrieve_hook(&self, h: Box<PostRetrieveHook>) -> Result<()> {
debug!("Registering post-retrieve hook: {:?}", h);
self.post_retrieve_hooks
.deref()
.lock()
.map_err(|_| StoreError::new(StoreErrorKind::HookRegisterError, None))
// TODO: cause: Some(Box::new(e))
.map(|mut guard| guard.deref_mut().push(h))
}
pub fn register_pre_update_hook(&self, h: Box<PreUpdateHook>) -> Result<()> {
debug!("Registering pre-update hook: {:?}", h);
self.pre_update_hooks
.deref()
.lock()
.map_err(|_| StoreError::new(StoreErrorKind::HookRegisterError, None))
// TODO: cause: Some(Box::new(e))
.map(|mut guard| guard.deref_mut().push(h))
}
pub fn register_post_update_hook(&self, h: Box<PostUpdateHook>) -> Result<()> {
debug!("Registering post-update hook: {:?}", h);
self.post_update_hooks
.deref()
.lock()
.map_err(|_| StoreError::new(StoreErrorKind::HookRegisterError, None))
// TODO: cause: Some(Box::new(e))
.map(|mut guard| guard.deref_mut().push(h))
}
pub fn register_pre_delete_hook(&self, h: Box<PreDeleteHook>) -> Result<()> {
debug!("Registering pre-delete hook: {:?}", h);
self.pre_delete_hooks
.deref()
.lock()
.map_err(|_| StoreError::new(StoreErrorKind::HookRegisterError, None))
// TODO: cause: Some(Box::new(e))
.map(|mut guard| guard.deref_mut().push(h))
}
pub fn register_post_delete_hook(&self, h: Box<PostDeleteHook>) -> Result<()> {
debug!("Registering post-delete hook: {:?}", h);
self.post_delete_hooks
.deref()
.lock()
.map_err(|_| StoreError::new(StoreErrorKind::HookRegisterError, None))
// TODO: cause: Some(Box::new(e))
.map(|mut guard| guard.deref_mut().push(h))
}
pub fn execute_pre_read_hooks(&self, id: &StoreId) -> Result<()> {
debug!("Execute pre-read hooks: {:?}", self.pre_read_hooks);
let guard = self.pre_read_hooks.deref().lock();
if guard.is_err() { return Err(StoreError::new(StoreErrorKind::PreHookExecuteError, None)) }
guard.unwrap().deref().iter()
.fold(Ok(()), |acc, hook| {
debug!("[Hook][exec]: {:?}", hook);
acc.and_then(|_| hook.pre_read(id))
})
.map_err(|e| StoreError::new(StoreErrorKind::PreHookExecuteError, Some(Box::new(e))))
}
pub fn execute_post_read_hooks<'a>(&'a self, fle: FileLockEntry<'a>)
-> Result<FileLockEntry<'a>>
{ {
debug!("Execute post-read hooks: {:?}", self.post_read_hooks); debug!("Registering hook: {:?}", h);
self.post_read_hooks debug!(" in position: {:?}", position);
debug!(" with aspect: {:?}", aspect_name);
let mut guard = match position {
HookPosition::PreRead => self.pre_read_aspects.clone(),
HookPosition::PostRead => self.post_read_aspects.clone(),
HookPosition::PreCreate => self.pre_create_aspects.clone(),
HookPosition::PostCreate => self.post_create_aspects.clone(),
HookPosition::PreRetrieve => self.pre_retrieve_aspects.clone(),
HookPosition::PostRetrieve => self.post_retrieve_aspects.clone(),
HookPosition::PreUpdate => self.pre_update_aspects.clone(),
HookPosition::PostUpdate => self.post_update_aspects.clone(),
HookPosition::PreDelete => self.pre_delete_aspects.clone(),
HookPosition::PostDelete => self.post_delete_aspects.clone(),
};
let mut guard = guard
.deref() .deref()
.lock() .lock()
.map_err(|e| StoreError::new(StoreErrorKind::PostHookExecuteError, None)) .map_err(|_| StoreError::new(StoreErrorKind::HookRegisterError, None));
.and_then(|guard| {
let accessors = guard.deref().iter().map(|h| h.accessor()).collect(); if guard.is_err() {
self.execute_hook_accessors(accessors, fle) return Err(StoreError::new(StoreErrorKind::HookRegisterError,
.map_err(|e| { Some(Box::new(guard.err().unwrap()))));
StoreError::new(StoreErrorKind::PostHookExecuteError, Some(Box::new(e))) }
}) let mut guard = guard.unwrap();
}) for mut aspect in guard.deref_mut() {
if aspect.name().clone() == aspect_name.clone() {
aspect.register_hook(h);
return Ok(());
}
}
return Err(StoreError::new(StoreErrorKind::HookRegisterError, None));
} }
fn execute_hook_accessors<'a>(&self, fn execute_hooks_for_id(&self,
accessors: Vec<Box<HookDataAccessor>>, aspects: Arc<Mutex<Vec<Aspect>>>,
fle: FileLockEntry<'a>) id: &StoreId)
-> Result<FileLockEntry<'a>> -> Result<()>
{ {
use std::thread; let guard = aspects.deref().lock();
use std::thread::JoinHandle;
use hook::accessor::HookDataAccessor as HDA;
use error::StoreError as SE;
use error::StoreErrorKind as SEK;
let iter_par = accessors.iter().all(|a| {
match a.deref() { &HDA::NonMutableAccess(_) => true, _ => false }
});
debug!("Parallel execution of hooks = {}", iter_par);
if iter_par {
debug!("Parallel execution of hooks not implemented yet");
let threads : Vec<Result<()>> = accessors
.iter()
.map(|accessor| {
crossbeam::scope(|scope| {
scope.spawn(|| {
match accessor.deref() {
&HDA::NonMutableAccess(ref accessor) => accessor.access(&fle),
_ => panic!("There shouldn't be a MutableHookDataAcceessor but there is"),
}
.map_err(|e| ()) // TODO: We're losing the error cause here
})
})
})
.map(|item| item.join().map_err(|_| SE::new(SEK::HookExecutionError, None)))
.collect();
threads
.into_iter()
.fold(Ok(fle), |acc, elem| {
acc.and_then(|a| {
elem.map(|_| a)
.map_err(|_| SE::new(SEK::HookExecutionError, None))
})
})
} else {
accessors.into_iter().fold(Ok(fle), move |acc, accessor| {
match accessor.deref() {
&HDA::MutableAccess(ref accessor) => {
match acc {
Ok(mut fle) => accessor
.access_mut(&mut fle)
.and(Ok(fle))
.map_err(|e| SE::new(SEK::HookExecutionError, Some(Box::new(e)))),
Err(e) => Err(SE::new(SEK::HookExecutionError, Some(Box::new(e)))),
}
},
&HDA::NonMutableAccess(ref accessor) => {
match acc {
Ok(mut fle) => accessor
.access(&fle)
.and(Ok(fle))
.map_err(|e| SE::new(SEK::HookExecutionError, Some(Box::new(e)))),
Err(e) => Err(SE::new(SEK::HookExecutionError, Some(Box::new(e)))),
}
},
_ => Err(StoreError::new(StoreErrorKind::HookExecutionError, None)),
}
})
}
}
pub fn execute_pre_create_hooks(&self, id: &StoreId) -> Result<()> {
debug!("Execute pre-create hooks: {:?}", self.pre_create_hooks);
let guard = self.pre_create_hooks.deref().lock();
if guard.is_err() { return Err(StoreError::new(StoreErrorKind::PreHookExecuteError, None)) } if guard.is_err() { return Err(StoreError::new(StoreErrorKind::PreHookExecuteError, None)) }
guard.unwrap().deref().iter() guard.unwrap().deref().iter()
.fold(Ok(()), |acc, hook| { .fold(Ok(()), |acc, aspect| {
debug!("[Hook][exec]: {:?}", hook); debug!("[Aspect][exec]: {:?}", aspect);
acc.and_then(|_| hook.pre_create(id)) acc.and_then(|_| (aspect as &StoreIdAccessor).access(id))
}) })
.map_err(|e| StoreError::new(StoreErrorKind::PreHookExecuteError, Some(Box::new(e)))) .map_err(|e| StoreError::new(StoreErrorKind::PreHookExecuteError, Some(Box::new(e))))
} }
pub fn execute_post_create_hooks<'a>(&'a self, fle: FileLockEntry<'a>) fn execute_hooks_for_mut_file(&self,
-> Result<FileLockEntry<'a>> aspects: Arc<Mutex<Vec<Aspect>>>,
fle: &mut FileLockEntry)
-> Result<()>
{ {
debug!("Execute post-create hooks: {:?}", self.post_create_hooks); let guard = aspects.deref().lock();
self.post_create_hooks
.deref()
.lock()
.map_err(|e| StoreError::new(StoreErrorKind::PostHookExecuteError, None))
.and_then(|guard| {
let accessors = guard.deref().iter().map(|h| h.accessor()).collect();
self.execute_hook_accessors(accessors, fle)
.map_err(|e| {
StoreError::new(StoreErrorKind::PostHookExecuteError, Some(Box::new(e)))
})
})
}
pub fn execute_pre_retrieve_hooks(&self, id: &StoreId) -> Result<()> {
debug!("Execute pre-retrieve hooks: {:?}", self.pre_retrieve_hooks);
let guard = self.pre_retrieve_hooks.deref().lock();
if guard.is_err() { return Err(StoreError::new(StoreErrorKind::PreHookExecuteError, None)) } if guard.is_err() { return Err(StoreError::new(StoreErrorKind::PreHookExecuteError, None)) }
guard.unwrap().deref().iter() guard.unwrap().deref().iter()
.fold(Ok(()), |acc, hook| { .fold(Ok(()), |acc, aspect| {
debug!("[Hook][exec]: {:?}", hook); debug!("[Aspect][exec]: {:?}", aspect);
acc.and_then(|_| hook.pre_retrieve(id)) acc.and_then(|_| aspect.access_mut(fle))
}) })
.map_err(|e| StoreError::new(StoreErrorKind::PreHookExecuteError, Some(Box::new(e)))) .map_err(|e| StoreError::new(StoreErrorKind::PreHookExecuteError, Some(Box::new(e))))
} }
pub fn execute_post_retrieve_hooks<'a>(&'a self, fle: FileLockEntry<'a>) fn execute_hooks_for_file(&self,
-> Result<FileLockEntry<'a>> aspects: Arc<Mutex<Vec<Aspect>>>,
fle: &FileLockEntry)
-> Result<()>
{ {
debug!("Execute post-retrieve hooks: {:?}", self.post_retrieve_hooks); let guard = aspects.deref().lock();
self.post_retrieve_hooks
.deref()
.lock()
.map_err(|e| StoreError::new(StoreErrorKind::PostHookExecuteError, None))
.and_then(|guard| {
let accessors = guard.deref().iter().map(|h| h.accessor()).collect();
self.execute_hook_accessors(accessors, fle)
.map_err(|e| {
StoreError::new(StoreErrorKind::PostHookExecuteError, Some(Box::new(e)))
})
})
}
pub fn execute_pre_update_hooks(&self, fle: &mut FileLockEntry) -> Result<()> {
debug!("Execute pre-update hooks: {:?}", self.pre_update_hooks);
let guard = self.pre_update_hooks.deref().lock();
if guard.is_err() { return Err(StoreError::new(StoreErrorKind::PreHookExecuteError, None)) } if guard.is_err() { return Err(StoreError::new(StoreErrorKind::PreHookExecuteError, None)) }
guard.unwrap().deref().iter() guard.unwrap().deref().iter()
.fold(Ok(()), |acc, hook| { .fold(Ok(()), |acc, aspect| {
debug!("[Hook][exec]: {:?}", hook); debug!("[Aspect][exec]: {:?}", aspect);
acc.and_then(|_| hook.pre_update(fle)) acc.and_then(|_| (aspect as &NonMutableHookDataAccessor).access(fle))
}) })
.map_err(|e| StoreError::new(StoreErrorKind::PreHookExecuteError, Some(Box::new(e)))) .map_err(|e| StoreError::new(StoreErrorKind::PreHookExecuteError, Some(Box::new(e))))
} }
pub fn execute_post_update_hooks<'a>(&self, fle: FileLockEntry<'a>) -> Result<FileLockEntry<'a>> {
debug!("Execute post-update hooks: {:?}", self.post_update_hooks);
self.post_update_hooks
.deref()
.lock()
.map_err(|e| StoreError::new(StoreErrorKind::PostHookExecuteError, None))
.and_then(|guard| {
let accessors = guard.deref().iter().map(|h| h.accessor()).collect();
self.execute_hook_accessors(accessors, fle)
.map_err(|e| {
StoreError::new(StoreErrorKind::PostHookExecuteError, Some(Box::new(e)))
})
})
}
pub fn execute_pre_delete_hooks(&self, id: &StoreId) -> Result<()> {
debug!("Execute pre-delete hooks: {:?}", self.pre_delete_hooks);
let guard = self.pre_delete_hooks.deref().lock();
if guard.is_err() { return Err(StoreError::new(StoreErrorKind::PreHookExecuteError, None)) }
guard.unwrap().deref().iter()
.fold(Ok(()), |acc, hook| {
debug!("[Hook][exec]: {:?}", hook);
acc.and_then(|_| hook.pre_delete(id))
})
.map_err(|e| StoreError::new(StoreErrorKind::PreHookExecuteError, Some(Box::new(e))))
}
pub fn execute_post_delete_hooks(&self, id: &StoreId) -> Result<()> {
debug!("Execute post-delete hooks: {:?}", self.post_delete_hooks);
self.post_delete_hooks
.deref()
.lock()
.map_err(|e| StoreError::new(StoreErrorKind::PostHookExecuteError, None))
.and_then(|guard| {
guard.deref()
.iter()
.fold(Ok(()), move |res, hook| {
debug!("[Hook][exec]: {:?}", hook);
res.and_then(|_| hook.post_delete(id))
})
.map_err(|e| {
StoreError::new(StoreErrorKind::PostHookExecuteError, Some(Box::new(e)))
})
})
}
} }
impl Drop for Store { impl Drop for Store {