diff --git a/libimagstore/src/store.rs b/libimagstore/src/store.rs index 1c19e1c6..50f4d83d 100644 --- a/libimagstore/src/store.rs +++ b/libimagstore/src/store.rs @@ -23,11 +23,15 @@ use error::{StoreError, StoreErrorKind}; use storeid::{StoreId, StoreIdIterator}; use lazyfile::LazyFile; +use hook::aspect::Aspect; use hook::result::HookResult; use hook::accessor::{ MutableHookDataAccessor, NonMutableHookDataAccessor, + StoreIdAccessor, HookDataAccessor, HookDataAccessorProvider}; +use hook::position::HookPosition; +use hook::Hook; /// The Result Type returned by any interaction with the store that could fail pub type Result = RResult; @@ -105,16 +109,16 @@ pub struct Store { * Registered hooks */ - pre_read_hooks : Arc>>>, - post_read_hooks : Arc>>>, - pre_create_hooks : Arc>>>, - post_create_hooks : Arc>>>, - pre_retrieve_hooks : Arc>>>, - post_retrieve_hooks : Arc>>>, - pre_update_hooks : Arc>>>, - post_update_hooks : Arc>>>, - pre_delete_hooks : Arc>>>, - post_delete_hooks : Arc>>>, + pre_read_aspects : Arc>>, + post_read_aspects : Arc>>, + pre_create_aspects : Arc>>, + post_create_aspects : Arc>>, + pre_retrieve_aspects : Arc>>, + post_retrieve_aspects : Arc>>, + pre_update_aspects : Arc>>, + post_update_aspects : Arc>>, + pre_delete_aspects : Arc>>, + post_delete_aspects : Arc>>, /** * Internal Path->File cache map @@ -151,16 +155,16 @@ impl Store { debug!("Store building succeeded"); Ok(Store { location: location, - pre_read_hooks : Arc::new(Mutex::new(vec![])), - post_read_hooks : Arc::new(Mutex::new(vec![])), - pre_create_hooks : Arc::new(Mutex::new(vec![])), - post_create_hooks : Arc::new(Mutex::new(vec![])), - pre_retrieve_hooks : Arc::new(Mutex::new(vec![])), - post_retrieve_hooks : Arc::new(Mutex::new(vec![])), - pre_update_hooks : Arc::new(Mutex::new(vec![])), - post_update_hooks : Arc::new(Mutex::new(vec![])), - pre_delete_hooks : Arc::new(Mutex::new(vec![])), - post_delete_hooks : Arc::new(Mutex::new(vec![])), + pre_read_aspects : Arc::new(Mutex::new(vec![])), + post_read_aspects : Arc::new(Mutex::new(vec![])), + pre_create_aspects : Arc::new(Mutex::new(vec![])), + post_create_aspects : Arc::new(Mutex::new(vec![])), + pre_retrieve_aspects : Arc::new(Mutex::new(vec![])), + post_retrieve_aspects : Arc::new(Mutex::new(vec![])), + pre_update_aspects : Arc::new(Mutex::new(vec![])), + post_update_aspects : Arc::new(Mutex::new(vec![])), + pre_delete_aspects : Arc::new(Mutex::new(vec![])), + post_delete_aspects : Arc::new(Mutex::new(vec![])), entries: Arc::new(RwLock::new(HashMap::new())), }) } @@ -176,7 +180,7 @@ impl Store { /// Creates the Entry at the given location (inside the entry) pub fn create<'a>(&'a self, id: StoreId) -> Result> { 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); } @@ -194,14 +198,17 @@ impl Store { 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 /// dropped, the new Entry is written to disk pub fn retrieve<'a>(&'a self, id: StoreId) -> Result> { 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); } @@ -214,7 +221,15 @@ impl Store { se.status = StoreEntryStatus::Borrowed; 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 @@ -224,7 +239,7 @@ impl Store { /// Return the `FileLockEntry` and write to disk 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); } @@ -232,8 +247,7 @@ impl Store { return Err(e); } - self.execute_post_update_hooks(entry) - .map(|_| ()) + self.execute_hooks_for_mut_file(self.post_update_aspects.clone(), &mut entry) } /// Internal method to write to the filesystem store. @@ -284,7 +298,7 @@ impl Store { /// Delete an entry pub fn delete(&self, id: StoreId) -> Result<()> { 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); } @@ -306,7 +320,7 @@ impl Store { 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 @@ -314,327 +328,96 @@ impl Store { &self.location } - pub fn register_pre_read_hook(&self, h: Box) -> Result<()> { - debug!("Registering pre-read hook: {:?}", h); - self.pre_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_post_read_hook(&self, h: Box) -> 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) -> 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) -> 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) -> 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) -> 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) -> 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) -> 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) -> 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) -> 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> + pub fn register_hook(&mut self, + position: HookPosition, + aspect_name: &String, + h: Box) + -> Result<()> { - debug!("Execute post-read hooks: {:?}", self.post_read_hooks); - self.post_read_hooks + debug!("Registering hook: {:?}", h); + 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() .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))) - }) - }) - } + .map_err(|_| StoreError::new(StoreErrorKind::HookRegisterError, None)); - fn execute_hook_accessors<'a>(&self, - accessors: Vec>, - fle: FileLockEntry<'a>) - -> Result> - { - use std::thread; - 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> = 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)), - } - }) + if guard.is_err() { + return Err(StoreError::new(StoreErrorKind::HookRegisterError, + Some(Box::new(guard.err().unwrap())))); } + 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)); } - 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)) } - - guard.unwrap().deref().iter() - .fold(Ok(()), |acc, hook| { - debug!("[Hook][exec]: {:?}", hook); - acc.and_then(|_| hook.pre_create(id)) - }) - .map_err(|e| StoreError::new(StoreErrorKind::PreHookExecuteError, Some(Box::new(e)))) - } - - pub fn execute_post_create_hooks<'a>(&'a self, fle: FileLockEntry<'a>) - -> Result> + fn execute_hooks_for_id(&self, + aspects: Arc>>, + id: &StoreId) + -> Result<()> { - debug!("Execute post-create hooks: {:?}", self.post_create_hooks); - 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(); + let guard = aspects.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_retrieve(id)) + .fold(Ok(()), |acc, aspect| { + debug!("[Aspect][exec]: {:?}", aspect); + acc.and_then(|_| (aspect as &StoreIdAccessor).access(id)) }) .map_err(|e| StoreError::new(StoreErrorKind::PreHookExecuteError, Some(Box::new(e)))) } - pub fn execute_post_retrieve_hooks<'a>(&'a self, fle: FileLockEntry<'a>) - -> Result> + fn execute_hooks_for_mut_file(&self, + aspects: Arc>>, + fle: &mut FileLockEntry) + -> Result<()> { - debug!("Execute post-retrieve hooks: {:?}", self.post_retrieve_hooks); - 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(); + let guard = aspects.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_update(fle)) + .fold(Ok(()), |acc, aspect| { + debug!("[Aspect][exec]: {:?}", aspect); + acc.and_then(|_| aspect.access_mut(fle)) }) .map_err(|e| StoreError::new(StoreErrorKind::PreHookExecuteError, Some(Box::new(e)))) } - pub fn execute_post_update_hooks<'a>(&self, fle: FileLockEntry<'a>) -> Result> { - 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(); + fn execute_hooks_for_file(&self, + aspects: Arc>>, + fle: &FileLockEntry) + -> Result<()> + { + let guard = aspects.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)) + .fold(Ok(()), |acc, aspect| { + debug!("[Aspect][exec]: {:?}", aspect); + acc.and_then(|_| (aspect as &NonMutableHookDataAccessor).access(fle)) }) .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 {