diff --git a/libimagstore/src/configuration.rs b/libimagstore/src/configuration.rs index 06e0d3c3..0dfc0490 100644 --- a/libimagstore/src/configuration.rs +++ b/libimagstore/src/configuration.rs @@ -20,160 +20,19 @@ use toml::Value; use libimagerror::into::IntoError; -use libimagutil::iter::FoldResult; use store::Result; /// Check whether the configuration is valid for the store -/// -/// The passed `Value` _must be_ the `[store]` sub-tree of the configuration. Otherwise this will -/// fail. -/// -/// It checks whether the configuration looks like the store wants it to be: -/// -/// ```toml -/// [store] -/// pre-create-hook-aspects = [ "misc", "encryption", "version-control"] -/// -/// [store.aspects.misc] -/// parallel = true -/// -/// [store.aspects.encryption] -/// parallel = false -/// -/// [store.aspects.version-control] -/// parallel = false -/// -/// [store.hooks.gnupg] -/// aspect = "encryption" -/// key = "0x123456789" -/// -/// [store.hooks.git] -/// aspect = "version-control" -/// ``` -/// -/// It checks: -/// * Whether all the maps are there (whether store, store.aspects, store.aspects.example are all -/// maps) -/// * Whether each aspect configuration has a "parallel = " setting -/// * Whether each hook congfiguration has a "aspect = " setting -/// -/// It does NOT check: -/// * Whether all aspects which are used in the hook configuration are also configured -/// -/// No configuration is a valid configuration, as the store will use the most conservative settings -/// automatically. This has also performance impact, as all hooks run in no-parallel mode then. -/// You have been warned! -/// -/// pub fn config_is_valid(config: &Option) -> Result<()> { - use std::collections::BTreeMap; use error::StoreErrorKind as SEK; if config.is_none() { return Ok(()); } - /// Check whether the config has a key with a string array. - /// The `key` is the key which is checked - /// The `kind` is the error kind which is used as `cause` if there is an error, so we can - /// indicate via error type which key is missing - fn has_key_with_string_ary(v: &BTreeMap, key: &str, - kind: SEK) -> Result<()> { - v.get(key) - .ok_or_else(|| { - warn!("Required key '{}' is not in store config", key); - SEK::ConfigKeyMissingError.into_error_with_cause(Box::new(kind.into_error())) - }) - .and_then(|t| match *t { - Value::Array(ref a) => { - a.iter().fold_result(|elem| if is_match!(*elem, Value::String(_)) { - Ok(()) - } else { - let cause = Box::new(kind.into_error()); - Err(SEK::ConfigTypeError.into_error_with_cause(cause)) - }) - }, - _ => { - warn!("Key '{}' in store config should contain an array", key); - Err(SEK::ConfigTypeError.into_error_with_cause(Box::new(kind.into_error()))) - } - }) - } - - /// Check that - /// * the top-level configuration - /// * is a table - /// * where all entries of a key `section` (eg. "hooks" or "aspects") - /// * Are maps - /// * where each has a key `key` (eg. "aspect" or "parallel") - /// * which fullfills constraint `f` (typecheck) - fn check_all_inner_maps_have_key_with(store_config: &BTreeMap, - section: &str, - key: &str, - f: F) - -> Result<()> - where F: Fn(&Value) -> bool - { - store_config.get(section) // The store config has the section `section` - .ok_or_else(|| { - warn!("Store config expects section '{}' to be present, but isn't.", section); - SEK::ConfigKeyMissingError.into_error() - }) - .and_then(|section_table| match *section_table { // which is - Value::Table(ref section_table) => // a table - section_table.iter().fold_result(|(inner_key, cfg)| { - match *cfg { - Value::Table(ref hook_config) => { // are tables - // with a key - let hook_aspect_is_valid = try!(hook_config.get(key) - .map(|hook_aspect| f(&hook_aspect)) - .ok_or(SEK::ConfigKeyMissingError.into_error()) - ); - - if !hook_aspect_is_valid { - Err(SEK::ConfigTypeError.into_error()) - } else { - Ok(()) - } - }, - _ => { - warn!("Store config expects '{}' to be in '{}.{}', but isn't.", - key, section, inner_key); - Err(SEK::ConfigKeyMissingError.into_error()) - } - } - }), - _ => { - warn!("Store config expects '{}' to be a Table, but isn't.", section); - Err(SEK::ConfigTypeError.into_error()) - } - }) - } - match *config { - Some(Value::Table(ref t)) => { - try!(has_key_with_string_ary(t, "store-unload-hook-aspects", SEK::ConfigKeyUnloadAspectsError)); - - try!(has_key_with_string_ary(t, "pre-create-hook-aspects", SEK::ConfigKeyPreCreateAspectsError)); - try!(has_key_with_string_ary(t, "post-create-hook-aspects", SEK::ConfigKeyPostCreateAspectsError)); - try!(has_key_with_string_ary(t, "pre-retrieve-hook-aspects", SEK::ConfigKeyPreRetrieveAspectsError)); - try!(has_key_with_string_ary(t, "post-retrieve-hook-aspects", SEK::ConfigKeyPostRetrieveAspectsError)); - try!(has_key_with_string_ary(t, "pre-update-hook-aspects", SEK::ConfigKeyPreUpdateAspectsError)); - try!(has_key_with_string_ary(t, "post-update-hook-aspects", SEK::ConfigKeyPostUpdateAspectsError)); - try!(has_key_with_string_ary(t, "pre-delete-hook-aspects", SEK::ConfigKeyPreDeleteAspectsError)); - try!(has_key_with_string_ary(t, "post-delete-hook-aspects", SEK::ConfigKeyPostDeleteAspectsError)); - - // The section "hooks" has maps which have a key "aspect" which has a value of type - // String - try!(check_all_inner_maps_have_key_with(t, "hooks", "aspect", - |asp| is_match!(asp, &Value::String(_)))); - - // The section "aspects" has maps which have a key "parllel" which has a value of type - // Boolean - check_all_inner_maps_have_key_with(t, "aspects", "parallel", - |asp| is_match!(asp, &Value::Boolean(_))) - } + Some(Value::Table(_)) => Ok(()), _ => { warn!("Store config is no table"); Err(SEK::ConfigTypeError.into_error()) @@ -207,152 +66,6 @@ pub fn config_implicit_store_create_allowed(config: Option<&Value>) -> bool { }).unwrap_or(false) } -pub fn get_store_unload_aspect_names(value: &Option) -> Vec { - get_aspect_names_for_aspect_position("store-unload-hook-aspects", value) -} - -pub fn get_pre_create_aspect_names(value: &Option) -> Vec { - get_aspect_names_for_aspect_position("pre-create-hook-aspects", value) -} - -pub fn get_post_create_aspect_names(value: &Option) -> Vec { - get_aspect_names_for_aspect_position("post-create-hook-aspects", value) -} - -pub fn get_pre_retrieve_aspect_names(value: &Option) -> Vec { - get_aspect_names_for_aspect_position("pre-retrieve-hook-aspects", value) -} - -pub fn get_post_retrieve_aspect_names(value: &Option) -> Vec { - get_aspect_names_for_aspect_position("post-retrieve-hook-aspects", value) -} - -pub fn get_pre_update_aspect_names(value: &Option) -> Vec { - get_aspect_names_for_aspect_position("pre-update-hook-aspects", value) -} - -pub fn get_post_update_aspect_names(value: &Option) -> Vec { - get_aspect_names_for_aspect_position("post-update-hook-aspects", value) -} - -pub fn get_pre_delete_aspect_names(value: &Option) -> Vec { - get_aspect_names_for_aspect_position("pre-delete-hook-aspects", value) -} - -pub fn get_post_delete_aspect_names(value: &Option) -> Vec { - get_aspect_names_for_aspect_position("post-delete-hook-aspects", value) -} - -pub fn get_pre_move_aspect_names(value: &Option) -> Vec { - get_aspect_names_for_aspect_position("pre-move-hook-aspects", value) -} - -pub fn get_post_move_aspect_names(value: &Option) -> Vec { - get_aspect_names_for_aspect_position("post-move-hook-aspects", value) -} - -#[derive(Debug)] -pub struct AspectConfig { - parallel: bool, - mutable_hooks: bool, - config: Value, -} - -impl AspectConfig { - - pub fn new(init: Value) -> AspectConfig { - debug!("Trying to parse AspectConfig from: {:?}", init); - let parallel = AspectConfig::is_parallel(&init); - let muthooks = AspectConfig::allows_mutable_hooks(&init); - AspectConfig { - config: init, - mutable_hooks: muthooks, - parallel: parallel, - } - } - - fn is_parallel(init: &Value) -> bool { - match *init { - Value::Table(ref t) => - t.get("parallel") - .map_or(false, |value| { - match *value { - Value::Boolean(b) => b, - _ => false, - } - }), - _ => false, - } - } - - fn allows_mutable_hooks(init: &Value) -> bool { - match *init { - Value::Table(ref t) => - t.get("mutable_hooks") - .map_or(false, |value| { - match *value { - Value::Boolean(b) => b, - _ => false, - } - }), - _ => false, - } - } - - pub fn allow_mutable_hooks(&self) -> bool { - self.mutable_hooks - } - - /// Get the aspect configuration for an aspect. - /// - /// Pass the store configuration object, this searches in `[aspects][]`. - /// - /// Returns `None` if one of the keys in the chain is not available - pub fn get_for(v: &Option, a_name: String) -> Option { - debug!("Get aspect configuration for {:?} from {:?}", a_name, v); - let res = match *v { - Some(Value::Table(ref tabl)) => { - match tabl.get("aspects") { - Some(&Value::Table(ref tabl)) => { - tabl.get(&a_name[..]).map(|asp| AspectConfig::new(asp.clone())) - }, - - _ => None, - } - }, - _ => None, - }; - debug!("Found aspect configuration for {:?}: {:?}", a_name, res); - res - } - -} - -fn get_aspect_names_for_aspect_position(config_name: &'static str, value: &Option) -> Vec { - use itertools::Itertools; - let mut v = vec![]; - - match *value { - Some(Value::Table(ref t)) => { - match t.get(config_name) { - Some(&Value::Array(ref a)) => { - for elem in a { - match *elem { - Value::String(ref s) => v.push(s.clone()), - _ => warn!("Non-String in configuration, inside '{}'", config_name), - } - } - }, - _ => warn!("'{}' configuration key should contain Array, does not", config_name), - }; - }, - None => warn!("No store configuration, cannot get '{}'", config_name), - _ => warn!("Configuration is not a table"), - } - - v.into_iter().unique().collect() -} - #[cfg(test)] mod tests { use toml::de::from_str as toml_from_str; @@ -387,312 +100,5 @@ mod tests { assert!(config_implicit_store_create_allowed(Some(config).as_ref())); } - #[test] - fn test_get_store_unload_aspect_names_not_existent() { - let config = toml_from_str("").unwrap(); - let names = get_store_unload_aspect_names(&Some(config)); - assert!(names.is_empty()); - } - - #[test] - fn test_get_store_unload_aspect_names_empty() { - let config = toml_from_str(r#" - store-unload-hook-aspects = [ ] - "#).unwrap(); - let names = get_store_unload_aspect_names(&Some(config)); - assert!(names.is_empty()); - } - - #[test] - fn test_get_store_unload_aspect_names_one_elem() { - let config = toml_from_str(r#" - store-unload-hook-aspects = [ "example" ] - "#).unwrap(); - let names = get_store_unload_aspect_names(&Some(config)); - assert_eq!(1, names.len()); - assert_eq!("example", names.iter().next().unwrap()); - } - - #[test] - fn test_get_pre_create_aspect_names_not_existent() { - let config = toml_from_str("").unwrap(); - assert!(get_pre_create_aspect_names(&Some(config)).is_empty()); - } - - #[test] - fn test_get_pre_create_aspect_names_empty() { - let config = toml_from_str(r#" - pre-create-hook-aspects = [ ] - "#).unwrap(); - let names = get_pre_create_aspect_names(&Some(config)); - assert!(names.is_empty()); - } - - #[test] - fn test_get_pre_create_aspect_names_one_elem() { - let config = toml_from_str(r#" - pre-create-hook-aspects = [ "example" ] - "#).unwrap(); - let names = get_pre_create_aspect_names(&Some(config)); - assert_eq!(1, names.len()); - assert_eq!("example", names.iter().next().unwrap()); - } - - #[test] - fn test_get_post_create_aspect_names_not_existent() { - let config = toml_from_str("").unwrap(); - assert!(get_post_create_aspect_names(&Some(config)).is_empty()); - } - - #[test] - fn test_get_post_create_aspect_names_empty() { - let config = toml_from_str(r#" - post-create-hook-aspects = [ ] - "#).unwrap(); - let names = get_post_create_aspect_names(&Some(config)); - assert!(names.is_empty()); - } - - #[test] - fn test_get_post_create_aspect_names_one_elem() { - let config = toml_from_str(r#" - post-create-hook-aspects = [ "example" ] - "#).unwrap(); - let names = get_post_create_aspect_names(&Some(config)); - assert_eq!(1, names.len()); - assert_eq!("example", names.iter().next().unwrap()); - } - - #[test] - fn test_get_pre_retrieve_aspect_names_not_existent() { - let config = toml_from_str("").unwrap(); - assert!(get_pre_retrieve_aspect_names(&Some(config)).is_empty()); - } - - #[test] - fn test_get_pre_retrieve_aspect_names_empty() { - let config = toml_from_str(r#" - pre-retrieve-hook-aspects = [ ] - "#).unwrap(); - let names = get_pre_retrieve_aspect_names(&Some(config)); - assert!(names.is_empty()); - } - - #[test] - fn test_get_pre_retrieve_aspect_names_one_elem() { - let config = toml_from_str(r#" - pre-retrieve-hook-aspects = [ "example" ] - "#).unwrap(); - let names = get_pre_retrieve_aspect_names(&Some(config)); - assert_eq!(1, names.len()); - assert_eq!("example", names.iter().next().unwrap()); - } - - #[test] - fn test_get_post_retrieve_aspect_names_not_existent() { - let config = toml_from_str("").unwrap(); - assert!(get_post_retrieve_aspect_names(&Some(config)).is_empty()); - } - - #[test] - fn test_get_post_retrieve_aspect_names_empty() { - let config = toml_from_str(r#" - post-retrieve-hook-aspects = [ ] - "#).unwrap(); - let names = get_post_retrieve_aspect_names(&Some(config)); - assert!(names.is_empty()); - } - - #[test] - fn test_get_post_retrieve_aspect_names_one_elem() { - let config = toml_from_str(r#" - post-retrieve-hook-aspects = [ "example" ] - "#).unwrap(); - let names = get_post_retrieve_aspect_names(&Some(config)); - assert_eq!(1, names.len()); - assert_eq!("example", names.iter().next().unwrap()); - } - - #[test] - fn test_get_pre_update_aspect_names_not_existent() { - let config = toml_from_str("").unwrap(); - assert!(get_pre_update_aspect_names(&Some(config)).is_empty()); - } - - #[test] - fn test_get_pre_update_aspect_names_empty() { - let config = toml_from_str(r#" - pre-update-hook-aspects = [ ] - "#).unwrap(); - let names = get_pre_update_aspect_names(&Some(config)); - assert!(names.is_empty()); - } - - #[test] - fn test_get_pre_update_aspect_names_one_elem() { - let config = toml_from_str(r#" - pre-update-hook-aspects = [ "example" ] - "#).unwrap(); - let names = get_pre_update_aspect_names(&Some(config)); - assert_eq!(1, names.len()); - assert_eq!("example", names.iter().next().unwrap()); - } - - #[test] - fn test_get_post_update_aspect_names_not_existent() { - let config = toml_from_str("").unwrap(); - assert!(get_post_update_aspect_names(&Some(config)).is_empty()); - } - - #[test] - fn test_get_post_update_aspect_names_empty() { - let config = toml_from_str(r#" - post-update-hook-aspects = [ ] - "#).unwrap(); - let names = get_post_update_aspect_names(&Some(config)); - assert!(names.is_empty()); - } - - #[test] - fn test_get_post_update_aspect_names_one_elem() { - let config = toml_from_str(r#" - post-update-hook-aspects = [ "example" ] - "#).unwrap(); - let names = get_post_update_aspect_names(&Some(config)); - assert_eq!(1, names.len()); - assert_eq!("example", names.iter().next().unwrap()); - } - - #[test] - fn test_get_pre_delete_aspect_names_not_existent() { - let config = toml_from_str("").unwrap(); - assert!(get_pre_delete_aspect_names(&Some(config)).is_empty()); - } - - #[test] - fn test_get_pre_delete_aspect_names_empty() { - let config = toml_from_str(r#" - pre-delete-hook-aspects = [ ] - "#).unwrap(); - let names = get_pre_delete_aspect_names(&Some(config)); - assert!(names.is_empty()); - } - - #[test] - fn test_get_pre_delete_aspect_names_one_elem() { - let config = toml_from_str(r#" - pre-delete-hook-aspects = [ "example" ] - "#).unwrap(); - let names = get_pre_delete_aspect_names(&Some(config)); - assert_eq!(1, names.len()); - assert_eq!("example", names.iter().next().unwrap()); - } - - #[test] - fn test_get_post_delete_aspect_names_not_existent() { - let config = toml_from_str("").unwrap(); - assert!(get_post_delete_aspect_names(&Some(config)).is_empty()); - } - - #[test] - fn test_get_post_delete_aspect_names_empty() { - let config = toml_from_str(r#" - post-delete-hook-aspects = [ ] - "#).unwrap(); - let names = get_post_delete_aspect_names(&Some(config)); - assert!(names.is_empty()); - } - - #[test] - fn test_get_post_delete_aspect_names_one_elem() { - let config = toml_from_str(r#" - post-delete-hook-aspects = [ "example" ] - "#).unwrap(); - let names = get_post_delete_aspect_names(&Some(config)); - assert_eq!(1, names.len()); - assert_eq!("example", names.iter().next().unwrap()); - } - - #[test] - fn test_get_pre_move_aspect_names_not_existent() { - let config = toml_from_str("").unwrap(); - assert!(get_pre_move_aspect_names(&Some(config)).is_empty()); - } - - #[test] - fn test_get_pre_move_aspect_names_empty() { - let config = toml_from_str(r#" - pre-move-hook-aspects = [ ] - "#).unwrap(); - let names = get_pre_move_aspect_names(&Some(config)); - assert!(names.is_empty()); - } - - #[test] - fn test_get_pre_move_aspect_names_one_elem() { - let config = toml_from_str(r#" - pre-move-hook-aspects = [ "example" ] - "#).unwrap(); - let names = get_pre_move_aspect_names(&Some(config)); - assert_eq!(1, names.len()); - assert_eq!("example", names.iter().next().unwrap()); - } - - #[test] - fn test_get_post_move_aspect_names_not_existent() { - let config = toml_from_str("").unwrap(); - assert!(get_post_move_aspect_names(&Some(config)).is_empty()); - } - - #[test] - fn test_get_post_move_aspect_names_empty() { - let config = toml_from_str(r#" - post-move-hook-aspects = [ ] - "#).unwrap(); - let names = get_post_move_aspect_names(&Some(config)); - assert!(names.is_empty()); - } - - #[test] - fn test_get_post_move_aspect_names_one_elem() { - let config = toml_from_str(r#" - post-move-hook-aspects = [ "example" ] - "#).unwrap(); - let names = get_post_move_aspect_names(&Some(config)); - assert_eq!(1, names.len()); - assert_eq!("example", names.iter().next().unwrap()); - } - - #[test] - fn test_get_aspect_names_for_aspect_position_arbitrary_empty() { - let config = toml_from_str(r#" - test-key = [ ] - "#).unwrap(); - let names = get_aspect_names_for_aspect_position("test-key", &Some(config)); - assert!(names.is_empty()); - } - - #[test] - fn test_get_aspect_names_for_aspect_position_arbitrary_one() { - let config = toml_from_str(r#" - test-key = [ "test-value" ] - "#).unwrap(); - let names = get_aspect_names_for_aspect_position("test-key", &Some(config)); - assert_eq!(1, names.len()); - assert_eq!("test-value", names.iter().next().unwrap()); - } - - #[test] - fn test_get_aspect_names_for_aspect_position_arbitrary_duplicated() { - let config = toml_from_str(r#" - test-key = [ "test-value", "test-value" ] - "#).unwrap(); - let names = get_aspect_names_for_aspect_position("test-key", &Some(config)); - assert_eq!(1, names.len()); - let mut iter = names.iter(); - assert_eq!("test-value", iter.next().unwrap()); - assert!(iter.next().is_none()); - } - } diff --git a/libimagstore/src/error.rs b/libimagstore/src/error.rs index 09ed092f..3fe5a360 100644 --- a/libimagstore/src/error.rs +++ b/libimagstore/src/error.rs @@ -28,16 +28,6 @@ generate_custom_error_types!(StoreError, StoreErrorKind, CustomErrorData, ConfigTypeError => "Store configuration type error", ConfigKeyMissingError => "Configuration Key missing", - ConfigKeyUnloadAspectsError => "Config Key 'store-unload-hook-aspects' caused an error", - ConfigKeyPreCreateAspectsError => "Config Key 'pre-create-hook-aspects' caused an error", - ConfigKeyPostCreateAspectsError => "Config Key 'post-create-hook-aspects' caused an error", - ConfigKeyPreRetrieveAspectsError => "Config Key 'pre-retrieve-hook-aspect' caused an error", - ConfigKeyPostRetrieveAspectsError => "Config Key 'post-retrieve-hook-aspec' caused an error", - ConfigKeyPreUpdateAspectsError => "Config Key 'pre-update-hook-aspects' caused an error", - ConfigKeyPostUpdateAspectsError => "Config Key 'post-update-hook-aspects' caused an error", - ConfigKeyPreDeleteAspectsError => "Config Key 'pre-delete-hook-aspects' caused an error", - ConfigKeyPostDeleteAspectsError => "Config Key 'post-delete-hook-aspects' caused an error", - CreateStoreDirDenied => "Creating store directory implicitely denied", FileError => "File Error", IoError => "IO Error", @@ -63,11 +53,6 @@ generate_custom_error_types!(StoreError, StoreErrorKind, CustomErrorData, HeaderPathTypeFailure => "Header has wrong type for path", HeaderKeyNotFound => "Header Key not found", HeaderTypeFailure => "Header type is wrong", - HookRegisterError => "Hook register error", - AspectNameNotFoundError => "Aspect name not found", - HookExecutionError => "Hook execution error", - PreHookExecuteError => "Pre-Hook execution error", - PostHookExecuteError => "Post-Hook execution error", StorePathLacksVersion => "The supplied store path has no version part", GlobError => "glob() error", EncodingError => "Encoding error", diff --git a/libimagstore/src/hook/accessor.rs b/libimagstore/src/hook/accessor.rs deleted file mode 100644 index 8bcca25b..00000000 --- a/libimagstore/src/hook/accessor.rs +++ /dev/null @@ -1,49 +0,0 @@ -// -// imag - the personal information management suite for the commandline -// Copyright (C) 2015, 2016 Matthias Beyer and contributors -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; version -// 2.1 of the License. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -// - -use std::fmt::Debug; - -use hook::result::HookResult; -use store::FileLockEntry; -use storeid::StoreId; - -pub trait StoreIdAccessor : Debug + Send { - fn access(&self, &StoreId) -> HookResult<()>; -} - -pub trait MutableHookDataAccessor : Debug + Send { - fn access_mut(&self, &mut FileLockEntry) -> HookResult<()>; -} - -pub trait NonMutableHookDataAccessor : Debug + Send { - fn access(&self, &FileLockEntry) -> HookResult<()>; -} - -#[derive(Debug)] -pub enum HookDataAccessor<'a> { - StoreIdAccess(&'a StoreIdAccessor), - MutableAccess(&'a MutableHookDataAccessor), - NonMutableAccess(&'a NonMutableHookDataAccessor), -} - -pub trait HookDataAccessorProvider { - fn accessor(&self) -> HookDataAccessor; -} - - diff --git a/libimagstore/src/hook/aspect.rs b/libimagstore/src/hook/aspect.rs deleted file mode 100644 index 75cd595f..00000000 --- a/libimagstore/src/hook/aspect.rs +++ /dev/null @@ -1,151 +0,0 @@ -// -// imag - the personal information management suite for the commandline -// Copyright (C) 2015, 2016 Matthias Beyer and contributors -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; version -// 2.1 of the License. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -// - -use libimagerror::trace::trace_error; -use libimagutil::iter::FoldResult; - -use store::FileLockEntry; -use storeid::StoreId; -use hook::Hook; -use hook::result::HookResult; -use hook::accessor::{StoreIdAccessor, MutableHookDataAccessor, NonMutableHookDataAccessor}; -use hook::accessor::HookDataAccessor as HDA; - -use hook::error::HookError as HE; -use hook::error::HookErrorKind as HEK; -use configuration::AspectConfig; - -#[derive(Debug)] -pub struct Aspect { - cfg: Option, - name: String, - hooks: Vec>, -} - -impl Aspect { - - pub fn new(name: String, cfg: Option) -> Aspect { - Aspect { - cfg: cfg, - name: name, - hooks: vec![], - } - } - - pub fn name(&self) -> &String { - &self.name - } - - pub fn register_hook(&mut self, h: Box) { - self.hooks.push(h); - } - -} - -impl StoreIdAccessor for Aspect { - fn access(&self, id: &StoreId) -> HookResult<()> { - let accessors : Vec = self.hooks.iter().map(|h| h.accessor()).collect(); - if !accessors.iter().all(|a| { - let x = is_match!(*a, HDA::StoreIdAccess(_)); - if !x { - warn!("Denied execution of None-StoreId-Accessing Hook"); - debug!("Accessor: {:?}", a); - debug!("in StoreIdAccess-Aspect execution: {:?}", self); - } - x - }) { - return Err(HE::new(HEK::AccessTypeViolation, None)); - } - - accessors.iter().fold_result(|accessor| { - let res = match accessor { - &HDA::StoreIdAccess(accessor) => accessor.access(id), - _ => unreachable!(), - }; - trace_hook_errors(res) - }) - } -} - -impl MutableHookDataAccessor for Aspect { - fn access_mut(&self, fle: &mut FileLockEntry) -> HookResult<()> { - debug!("Checking whether mutable hooks are allowed"); - debug!("-> config = {:?}", self.cfg); - - let accessors : Vec = self.hooks.iter().map(|h| h.accessor()).collect(); - - // TODO: Naiive implementation. - // More sophisticated version would check whether there are _chunks_ of - // NonMutableAccess accessors and execute these chunks in parallel. We do not have - // performance concerns yet, so this is okay. - accessors.iter().fold_result(|accessor| { - let res = match accessor { - &HDA::StoreIdAccess(ref accessor) => accessor.access(fle.get_location()), - &HDA::NonMutableAccess(ref accessor) => accessor.access(fle), - &HDA::MutableAccess(ref accessor) => { - if !self.cfg.as_ref().map(|c| c.allow_mutable_hooks()).unwrap_or(false) { - debug!("Apparently mutable hooks are not allowed... failing now."); - return Err(HE::new(HEK::MutableHooksNotAllowed, None)); - } - - accessor.access_mut(fle) - }, - }; - trace_hook_errors(res) - }) - } -} - -impl NonMutableHookDataAccessor for Aspect { - fn access(&self, fle: &FileLockEntry) -> HookResult<()> { - let accessors : Vec = self.hooks.iter().map(|h| h.accessor()).collect(); - if !accessors.iter().all(|a| { - let x = is_match!(*a, HDA::NonMutableAccess(_)); - if !x { - warn!("Denied execution of Non-Mutable-Accessing Hook"); - debug!("Accessor: {:?}", a); - debug!("in StoreIdAccess-Aspect execution: {:?}", self); - } - x - }) { - return Err(HE::new(HEK::AccessTypeViolation, None)); - } - - accessors.iter().fold_result(|accessor| { - let res = match accessor { - &HDA::NonMutableAccess(accessor) => accessor.access(fle), - _ => unreachable!(), - }; - trace_hook_errors(res) - }) - } -} - -fn trace_hook_errors(res: HookResult<()>) -> HookResult<()> { - res.or_else(|e| { - if !e.is_aborting() { - trace_error(&e); - // ignore error if it is not aborting, as we printed it already - Ok(()) - } else { - Err(e) - } - }) -} - diff --git a/libimagstore/src/hook/error.rs b/libimagstore/src/hook/error.rs deleted file mode 100644 index 0d890bf3..00000000 --- a/libimagstore/src/hook/error.rs +++ /dev/null @@ -1,65 +0,0 @@ -// -// imag - the personal information management suite for the commandline -// Copyright (C) 2015, 2016 Matthias Beyer and contributors -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; version -// 2.1 of the License. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -// - -use std::default::Default; - -generate_error_imports!(); - -generate_custom_error_types!(HookError, HookErrorKind, CustomData, - HookExecutionError => "Hook exec error", - AccessTypeViolation => "Hook access type violation", - MutableHooksNotAllowed => "Mutable Hooks are denied" -); - -generate_result_helper!(HookError, HookErrorKind); - -#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Copy)] -pub struct CustomData { - aborting: bool, -} - -impl CustomData { - - pub fn aborting(mut self, b: bool) -> CustomData { - self.aborting = b; - self - } - -} - -impl Default for CustomData { - - fn default() -> CustomData { - CustomData { - aborting: true - } - } - -} - -impl HookError { - - pub fn is_aborting(&self) -> bool { - match self.custom_data { - Some(b) => b.aborting, - None => true - } - } - -} diff --git a/libimagstore/src/hook/mod.rs b/libimagstore/src/hook/mod.rs deleted file mode 100644 index 5c8e50f2..00000000 --- a/libimagstore/src/hook/mod.rs +++ /dev/null @@ -1,36 +0,0 @@ -// -// imag - the personal information management suite for the commandline -// Copyright (C) 2015, 2016 Matthias Beyer and contributors -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; version -// 2.1 of the License. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -// - -use std::fmt::Debug; - -use toml::Value; - -pub mod accessor; -pub mod aspect; -pub mod error; -pub mod position; -pub mod result; - -use hook::accessor::HookDataAccessorProvider; - -pub trait Hook : HookDataAccessorProvider + Debug + Send { - fn name(&self) -> &'static str; - fn set_config(&mut self, cfg: &Value); -} - diff --git a/libimagstore/src/hook/position.rs b/libimagstore/src/hook/position.rs deleted file mode 100644 index 8b9f9264..00000000 --- a/libimagstore/src/hook/position.rs +++ /dev/null @@ -1,32 +0,0 @@ -// -// imag - the personal information management suite for the commandline -// Copyright (C) 2015, 2016 Matthias Beyer and contributors -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; version -// 2.1 of the License. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -// - -#[derive(Debug, Clone)] -pub enum HookPosition { - StoreUnload, - - PreCreate, - PostCreate, - PreRetrieve, - PostRetrieve, - PreUpdate, - PostUpdate, - PreDelete, - PostDelete, -} diff --git a/libimagstore/src/hook/result.rs b/libimagstore/src/hook/result.rs deleted file mode 100644 index e25c7220..00000000 --- a/libimagstore/src/hook/result.rs +++ /dev/null @@ -1,22 +0,0 @@ -// -// imag - the personal information management suite for the commandline -// Copyright (C) 2015, 2016 Matthias Beyer and contributors -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; version -// 2.1 of the License. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -// - -use hook::error::HookError; - -pub type HookResult = Result; diff --git a/libimagstore/src/lib.rs b/libimagstore/src/lib.rs index 44fecc36..948865f4 100644 --- a/libimagstore/src/lib.rs +++ b/libimagstore/src/lib.rs @@ -52,7 +52,6 @@ extern crate libimagutil; pub mod storeid; pub mod error; -pub mod hook; pub mod store; mod configuration; mod file_abstraction; diff --git a/libimagstore/src/store.rs b/libimagstore/src/store.rs index ef5725f5..4d7e811d 100644 --- a/libimagstore/src/store.rs +++ b/libimagstore/src/store.rs @@ -26,7 +26,6 @@ use std::sync::RwLock; use std::io::Read; use std::convert::From; use std::convert::Into; -use std::sync::Mutex; use std::ops::Deref; use std::ops::DerefMut; use std::fmt::Formatter; @@ -45,17 +44,8 @@ use storeid::{IntoStoreId, StoreId, StoreIdIterator}; use file_abstraction::FileAbstraction; use toml_ext::*; -use hook::aspect::Aspect; -use hook::error::HookErrorKind; -use hook::result::HookResult; -use hook::accessor::{ MutableHookDataAccessor, - StoreIdAccessor}; -use hook::position::HookPosition; -use hook::Hook; - use libimagerror::into::IntoError; use libimagerror::trace::trace_error; -use libimagutil::iter::FoldResult; use libimagutil::debug_result::*; use self::glob_store_iter::*; @@ -195,23 +185,6 @@ pub struct Store { /// configuration: Option, - // - // Registered hooks - // - - store_unload_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>>, - pre_move_aspects : Arc>>, - post_move_aspects : Arc>>, - /// /// Internal Path->File cache map /// @@ -237,8 +210,6 @@ impl Store { /// /// If the path exists and is a file, the operation is aborted as well, an error is returned. /// - /// After that, the store hook aspects are created and registered in the store. - /// /// # Return values /// /// - On success: Store object @@ -273,88 +244,9 @@ impl Store { return Err(SEK::StorePathExists.into_error()); } - let store_unload_aspects = get_store_unload_aspect_names(&store_config) - .into_iter().map(|n| { - let cfg = AspectConfig::get_for(&store_config, n.clone()); - Aspect::new(n, cfg) - }).collect(); - - let pre_create_aspects = get_pre_create_aspect_names(&store_config) - .into_iter().map(|n| { - let cfg = AspectConfig::get_for(&store_config, n.clone()); - Aspect::new(n, cfg) - }).collect(); - - let post_create_aspects = get_post_create_aspect_names(&store_config) - .into_iter().map(|n| { - let cfg = AspectConfig::get_for(&store_config, n.clone()); - Aspect::new(n, cfg) - }).collect(); - - let pre_retrieve_aspects = get_pre_retrieve_aspect_names(&store_config) - .into_iter().map(|n| { - let cfg = AspectConfig::get_for(&store_config, n.clone()); - Aspect::new(n, cfg) - }).collect(); - - let post_retrieve_aspects = get_post_retrieve_aspect_names(&store_config) - .into_iter().map(|n| { - let cfg = AspectConfig::get_for(&store_config, n.clone()); - Aspect::new(n, cfg) - }).collect(); - - let pre_update_aspects = get_pre_update_aspect_names(&store_config) - .into_iter().map(|n| { - let cfg = AspectConfig::get_for(&store_config, n.clone()); - Aspect::new(n, cfg) - }).collect(); - - let post_update_aspects = get_post_update_aspect_names(&store_config) - .into_iter().map(|n| { - let cfg = AspectConfig::get_for(&store_config, n.clone()); - Aspect::new(n, cfg) - }).collect(); - - let pre_delete_aspects = get_pre_delete_aspect_names(&store_config) - .into_iter().map(|n| { - let cfg = AspectConfig::get_for(&store_config, n.clone()); - Aspect::new(n, cfg) - }).collect(); - - let post_delete_aspects = get_post_delete_aspect_names(&store_config) - .into_iter().map(|n| { - let cfg = AspectConfig::get_for(&store_config, n.clone()); - Aspect::new(n, cfg) - }).collect(); - - let pre_move_aspects = get_pre_move_aspect_names(&store_config) - .into_iter().map(|n| { - let cfg = AspectConfig::get_for(&store_config, n.clone()); - Aspect::new(n, cfg) - }).collect(); - - let post_move_aspects = get_post_move_aspect_names(&store_config) - .into_iter().map(|n| { - let cfg = AspectConfig::get_for(&store_config, n.clone()); - Aspect::new(n, cfg) - }).collect(); - let store = Store { location: location.clone(), configuration: store_config, - - store_unload_aspects : Arc::new(Mutex::new(store_unload_aspects)), - - pre_create_aspects : Arc::new(Mutex::new(pre_create_aspects)), - post_create_aspects : Arc::new(Mutex::new(post_create_aspects)), - pre_retrieve_aspects : Arc::new(Mutex::new(pre_retrieve_aspects)), - post_retrieve_aspects : Arc::new(Mutex::new(post_retrieve_aspects)), - pre_update_aspects : Arc::new(Mutex::new(pre_update_aspects)), - post_update_aspects : Arc::new(Mutex::new(post_update_aspects)), - pre_delete_aspects : Arc::new(Mutex::new(pre_delete_aspects)), - post_delete_aspects : Arc::new(Mutex::new(post_delete_aspects)), - pre_move_aspects : Arc::new(Mutex::new(pre_move_aspects)), - post_move_aspects : Arc::new(Mutex::new(post_move_aspects)), entries: Arc::new(RwLock::new(HashMap::new())), }; @@ -429,32 +321,17 @@ impl Store { /// Creates the Entry at the given location (inside the entry) /// - /// # Executed Hooks - /// - /// - Pre create aspects - /// - post create aspects - /// /// # Return value /// /// On success: FileLockEntry /// /// On error: /// - Errors StoreId::into_storeid() might return - /// - CreateCallError(HookExecutionError(PreHookExecuteError(_))) - /// of the first failing pre hook. - /// - CreateCallError(HookExecutionError(PostHookExecuteError(_))) - /// of the first failing post hook. /// - CreateCallError(LockPoisoned()) if the internal lock is poisened. /// - CreateCallError(EntryAlreadyExists()) if the entry exists already. /// pub fn create<'a, S: IntoStoreId>(&'a self, id: S) -> Result> { let id = try!(id.into_storeid()).with_base(self.path().clone()); - if let Err(e) = self.execute_hooks_for_id(self.pre_create_aspects.clone(), &id) { - return Err(e) - .map_err_into(SEK::PreHookExecuteError) - .map_err_into(SEK::HookExecutionError) - .map_err_into(SEK::CreateCallError) - } { let mut hsmap = match self.entries.write() { @@ -472,12 +349,7 @@ impl Store { }); } - let mut fle = FileLockEntry::new(self, Entry::new(id)); - self.execute_hooks_for_mut_file(self.post_create_aspects.clone(), &mut fle) - .map_err_into(SEK::PostHookExecuteError) - .map_err_into(SEK::HookExecutionError) - .map_err_into(SEK::CreateCallError) - .map(|_| fle) + Ok(FileLockEntry::new(self, Entry::new(id))) } /// Borrow a given Entry. When the `FileLockEntry` is either `update`d or @@ -486,32 +358,16 @@ impl Store { /// Implicitely creates a entry in the store if there is no entry with the id `id`. For a /// non-implicitely-create look at `Store::get`. /// - /// # Executed Hooks - /// - /// - Pre retrieve aspects - /// - post retrieve aspects - /// /// # Return value /// /// On success: FileLockEntry /// /// On error: /// - Errors StoreId::into_storeid() might return - /// - RetrieveCallError(HookExecutionError(PreHookExecuteError(_))) - /// of the first failing pre hook. - /// - RetrieveCallError(HookExecutionError(PostHookExecuteError(_))) - /// of the first failing post hook. /// - RetrieveCallError(LockPoisoned()) if the internal lock is poisened. /// pub fn retrieve<'a, S: IntoStoreId>(&'a self, id: S) -> Result> { let id = try!(id.into_storeid()).with_base(self.path().clone()); - if let Err(e) = self.execute_hooks_for_id(self.pre_retrieve_aspects.clone(), &id) { - return Err(e) - .map_err_into(SEK::PreHookExecuteError) - .map_err_into(SEK::HookExecutionError) - .map_err_into(SEK::RetrieveCallError) - } - let entry = try!({ self.entries .write() @@ -526,21 +382,11 @@ impl Store { .map_err_into(SEK::RetrieveCallError) }); - let mut fle = FileLockEntry::new(self, entry); - self.execute_hooks_for_mut_file(self.post_retrieve_aspects.clone(), &mut fle) - .map_err_into(SEK::PostHookExecuteError) - .map_err_into(SEK::HookExecutionError) - .map_err_into(SEK::RetrieveCallError) - .and(Ok(fle)) + Ok(FileLockEntry::new(self, entry)) } /// Get an entry from the store if it exists. /// - /// # Executed Hooks - /// - /// - Pre get aspects - /// - post get aspects - /// /// # Return value /// /// On success: Some(FileLockEntry) or None @@ -617,32 +463,17 @@ impl Store { /// This method assumes that entry is dropped _right after_ the call, hence /// it is not public. /// - /// # Executed Hooks - /// - /// - Pre update aspects - /// - post update aspects - /// /// # Return value /// /// On success: Entry /// /// On error: - /// - UpdateCallError(HookExecutionError(PreHookExecuteError(_))) - /// of the first failing pre hook. - /// - UpdateCallError(HookExecutionError(PostHookExecuteError(_))) - /// of the first failing post hook. /// - UpdateCallError(LockPoisoned()) if the internal write lock cannot be aquierd. /// - IdNotFound() if the entry was not found in the stor /// - Errors Entry::verify() might return /// - Errors StoreEntry::write_entry() might return /// - fn _update<'a>(&'a self, mut entry: &mut FileLockEntry<'a>, modify_presence: bool) -> Result<()> { - let _ = try!(self.execute_hooks_for_mut_file(self.pre_update_aspects.clone(), &mut entry) - .map_err_into(SEK::PreHookExecuteError) - .map_err_into(SEK::HookExecutionError) - .map_err_into(SEK::UpdateCallError) - ); - + fn _update<'a>(&'a self, entry: &mut FileLockEntry<'a>, modify_presence: bool) -> Result<()> { let mut hsmap = match self.entries.write() { Err(_) => return Err(SE::new(SEK::LockPoisoned, None)), Ok(e) => e, @@ -661,21 +492,12 @@ impl Store { se.status = StoreEntryStatus::Present; } - self.execute_hooks_for_mut_file(self.post_update_aspects.clone(), &mut entry) - .map_err_into(SEK::PostHookExecuteError) - .map_err_into(SEK::HookExecutionError) - .map_err_into(SEK::UpdateCallError) + Ok(()) } /// Retrieve a copy of a given entry, this cannot be used to mutate /// the one on disk /// - /// TODO: Create Hooks for retrieving a copy - /// - /// # Executed Hooks - /// - /// - (none yet) - /// /// # Return value /// /// On success: Entry @@ -705,32 +527,17 @@ impl Store { /// Delete an entry /// - /// # Executed Hooks - /// - /// - Pre delete aspects, if the id can be used - /// - Post delete aspects, if the operation succeeded - /// /// # Return value /// /// On success: () /// /// On error: - /// - DeleteCallError(HookExecutionError(PreHookExecuteError(_))) - /// of the first failing pre hook. - /// - DeleteCallError(HookExecutionError(PostHookExecuteError(_))) - /// of the first failing post hook. /// - DeleteCallError(LockPoisoned()) if the internal write lock cannot be aquierd. /// - DeleteCallError(FileNotFound()) if the StoreId refers to a non-existing entry. /// - DeleteCallError(FileError()) if the internals failed to remove the file. /// pub fn delete(&self, id: S) -> Result<()> { let id = try!(id.into_storeid()).with_base(self.path().clone()); - if let Err(e) = self.execute_hooks_for_id(self.pre_delete_aspects.clone(), &id) { - return Err(e) - .map_err_into(SEK::PreHookExecuteError) - .map_err_into(SEK::HookExecutionError) - .map_err_into(SEK::DeleteCallError) - } { let mut entries = match self.entries.write() { @@ -758,25 +565,16 @@ impl Store { } } - self.execute_hooks_for_id(self.post_delete_aspects.clone(), &id) - .map_err_into(SEK::PostHookExecuteError) - .map_err_into(SEK::HookExecutionError) - .map_err_into(SEK::DeleteCallError) + Ok(()) } /// Save a copy of the Entry in another place - /// Executes the post_move_aspects for the new id - /// - /// TODO: Introduce new aspect for `save_to()`. pub fn save_to(&self, entry: &FileLockEntry, new_id: StoreId) -> Result<()> { self.save_to_other_location(entry, new_id, false) } /// Save an Entry in another place /// Removes the original entry - /// Executes the post_move_aspects for the new id - /// - /// TODO: Introduce new aspect for `save_as()`. pub fn save_as(&self, entry: FileLockEntry, new_id: StoreId) -> Result<()> { self.save_to_other_location(&entry, new_id, true) } @@ -809,9 +607,6 @@ impl Store { } }) .map_err_into(SEK::FileError) - .and_then(|_| self.execute_hooks_for_id(self.post_move_aspects.clone(), &new_id) - .map_err_into(SEK::PostHookExecuteError) - .map_err_into(SEK::HookExecutionError)) .map_err_into(SEK::MoveCallError) } @@ -826,14 +621,11 @@ impl Store { /// /// This function returns an error in certain cases: /// - /// * If pre-move-hooks error (if they return an error which indicates that the action should be - /// aborted) /// * If the about-to-be-moved entry is borrowed /// * If the lock on the internal data structure cannot be aquired /// * If the new path already exists /// * If the about-to-be-moved entry does not exist /// * If the FS-operation failed - /// * If the post-move-hooks error (though the operation has succeeded then). /// /// # Warnings /// @@ -855,13 +647,6 @@ impl Store { let new_id = new_id.with_base(self.path().clone()); let old_id = old_id.with_base(self.path().clone()); - if let Err(e) = self.execute_hooks_for_id(self.pre_move_aspects.clone(), &old_id) { - return Err(e) - .map_err_into(SEK::PreHookExecuteError) - .map_err_into(SEK::HookExecutionError) - .map_err_into(SEK::MoveByIdCallError) - } - { let mut hsmap = match self.entries.write() { Err(_) => return Err(SE::new(SEK::LockPoisoned, None)), @@ -900,10 +685,7 @@ impl Store { } - self.execute_hooks_for_id(self.pre_move_aspects.clone(), &new_id) - .map_err_into(SEK::PostHookExecuteError) - .map_err_into(SEK::HookExecutionError) - .map_err_into(SEK::MoveByIdCallError) + Ok(()) } /// Gets the path where this store is on the disk @@ -911,127 +693,6 @@ impl Store { &self.location } - /// Register a hook in the store. - /// - /// A hook is registered by a position (when should the hook be executed) and an aspect name. - /// The aspect name must be in the configuration file, so the configuration for the hook can be - /// passed to the `Hook` object. - /// - /// # Available Hook positions - /// - /// The hook positions are described in the type description of `HookPosition`. - /// - /// # Aspect names - /// - /// Aspect names are arbitrary, though sane things like "debug" or "vcs" are encouraged. - /// Refer to the documentation for more information. - /// - pub fn register_hook(&mut self, - position: HookPosition, - aspect_name: &str, - mut h: Box) - -> Result<()> - { - debug!("Registering hook: {:?}", h); - debug!(" in position: {:?}", position); - debug!(" with aspect: {:?}", aspect_name); - - let guard = match position { - HookPosition::StoreUnload => self.store_unload_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 = match guard.deref().lock().map_err(|_| SE::new(SEK::LockError, None)) { - Err(e) => return Err(SEK::HookRegisterError.into_error_with_cause(Box::new(e))), - Ok(g) => g, - }; - - for mut aspect in guard.deref_mut() { - if aspect.name().clone() == aspect_name.clone() { - debug!("Trying to find configuration for hook: {:?}", h); - self.get_config_for_hook(h.name()).map(|config| h.set_config(config)); - debug!("Trying to register hook in aspect: {:?} <- {:?}", aspect, h); - aspect.register_hook(h); - return Ok(()); - } - } - - let annfe = SEK::AspectNameNotFoundError.into_error(); - Err(SEK::HookRegisterError.into_error_with_cause(Box::new(annfe))) - } - - /// Get the configuration for a hook by the name of the hook, from the configuration file. - fn get_config_for_hook(&self, name: &str) -> Option<&Value> { - match self.configuration { - Some(Value::Table(ref tabl)) => { - debug!("Trying to head 'hooks' section from {:?}", tabl); - tabl.get("hooks") - .map(|hook_section| { - debug!("Found hook section: {:?}", hook_section); - debug!("Reading section key: {:?}", name); - match *hook_section { - Value::Table(ref tabl) => tabl.get(name), - _ => None - } - }) - .unwrap_or(None) - }, - _ => None, - } - } - - /// Execute all hooks from all aspects for a Store Id object. - /// - /// # Return value - /// - /// - () on success - /// - Error on the first failing hook. - /// - fn execute_hooks_for_id(&self, - aspects: Arc>>, - id: &StoreId) - -> HookResult<()> - { - match aspects.lock() { - Err(_) => return Err(HookErrorKind::HookExecutionError.into()), - Ok(g) => g - }.iter().fold_result(|aspect| { - debug!("[Aspect][exec]: {:?}", aspect); - (aspect as &StoreIdAccessor).access(id) - }).map_err(Box::new) - .map_err(|e| HookErrorKind::HookExecutionError.into_error_with_cause(e)) - } - - /// Execute all hooks from all aspects for a mutable `FileLockEntry` object. - /// - /// # Return value - /// - /// - () on success - /// - Error on the first failing hook. - /// - fn execute_hooks_for_mut_file(&self, - aspects: Arc>>, - fle: &mut FileLockEntry) - -> HookResult<()> - { - match aspects.lock() { - Err(_) => return Err(HookErrorKind::HookExecutionError.into()), - Ok(g) => g - }.iter().fold_result(|aspect| { - debug!("[Aspect][exec]: {:?}", aspect); - aspect.access_mut(fle) - }).map_err(Box::new) - .map_err(|e| HookErrorKind::HookExecutionError.into_error_with_cause(e)) - } - } impl Debug for Store { @@ -1042,14 +703,6 @@ impl Debug for Store { try!(write!(fmt, "\n")); try!(write!(fmt, " - location : {:?}\n", self.location)); try!(write!(fmt, " - configuration : {:?}\n", self.configuration)); - try!(write!(fmt, " - pre_create_aspects : {:?}\n", self.pre_create_aspects )); - try!(write!(fmt, " - post_create_aspects : {:?}\n", self.post_create_aspects )); - try!(write!(fmt, " - pre_retrieve_aspects : {:?}\n", self.pre_retrieve_aspects )); - try!(write!(fmt, " - post_retrieve_aspects : {:?}\n", self.post_retrieve_aspects )); - try!(write!(fmt, " - pre_update_aspects : {:?}\n", self.pre_update_aspects )); - try!(write!(fmt, " - post_update_aspects : {:?}\n", self.post_update_aspects )); - try!(write!(fmt, " - pre_delete_aspects : {:?}\n", self.pre_delete_aspects )); - try!(write!(fmt, " - post_delete_aspects : {:?}\n", self.post_delete_aspects )); try!(write!(fmt, "\n")); try!(write!(fmt, "Entries:\n")); try!(write!(fmt, "{:?}", self.entries)); @@ -1065,23 +718,8 @@ impl Drop for Store { /// Unlock all files on drop // /// TODO: Unlock them - /// TODO: Resolve this dirty hack with the StoreId for the Store drop hooks. /// fn drop(&mut self) { - match StoreId::new(Some(self.location.clone()), PathBuf::from(".")) { - Err(e) => { - trace_error(&e); - warn!("Cannot construct StoreId for Store to execute hooks!"); - warn!("Will close Store without executing hooks!"); - }, - Ok(store_id) => { - if let Err(e) = self.execute_hooks_for_id(self.store_unload_aspects.clone(), &store_id) { - debug!("Store-load hooks execution failed. Cannot create store object."); - warn!("Store Unload Hook error: {:?}", e); - } - }, - }; - debug!("Dropping store"); } @@ -1541,19 +1179,6 @@ mod store_tests { assert_eq!(store.location, PathBuf::from("/")); assert!(store.entries.read().unwrap().is_empty()); - - assert!(store.store_unload_aspects.lock().unwrap().is_empty()); - - assert!(store.pre_create_aspects.lock().unwrap().is_empty()); - assert!(store.post_create_aspects.lock().unwrap().is_empty()); - assert!(store.pre_retrieve_aspects.lock().unwrap().is_empty()); - assert!(store.post_retrieve_aspects.lock().unwrap().is_empty()); - assert!(store.pre_update_aspects.lock().unwrap().is_empty()); - assert!(store.post_update_aspects.lock().unwrap().is_empty()); - assert!(store.pre_delete_aspects.lock().unwrap().is_empty()); - assert!(store.post_delete_aspects.lock().unwrap().is_empty()); - assert!(store.pre_move_aspects.lock().unwrap().is_empty()); - assert!(store.post_move_aspects.lock().unwrap().is_empty()); } #[test] @@ -1775,476 +1400,3 @@ mod store_tests { } -#[cfg(test)] -mod store_hook_tests { - - mod test_hook { - use hook::Hook; - use hook::accessor::HookDataAccessor; - use hook::accessor::HookDataAccessorProvider; - use hook::position::HookPosition; - - use self::accessor::TestHookAccessor as DHA; - - use toml::Value; - - #[derive(Debug)] - pub struct TestHook { - position: HookPosition, - accessor: DHA, - } - - impl TestHook { - - pub fn new(pos: HookPosition, succeed: bool, error_aborting: bool) -> TestHook { - TestHook { position: pos.clone(), accessor: DHA::new(pos, succeed, error_aborting) } - } - - } - - impl Hook for TestHook { - fn name(&self) -> &'static str { "testhook_succeeding" } - fn set_config(&mut self, _: &Value) { } - } - - impl HookDataAccessorProvider for TestHook { - - fn accessor(&self) -> HookDataAccessor { - use hook::position::HookPosition as HP; - use hook::accessor::HookDataAccessor as HDA; - - match self.position { - HP::StoreUnload | - HP::PreCreate | - HP::PreRetrieve | - HP::PreDelete | - HP::PostDelete => HDA::StoreIdAccess(&self.accessor), - HP::PostCreate | - HP::PostRetrieve | - HP::PreUpdate | - HP::PostUpdate => HDA::MutableAccess(&self.accessor), - } - } - - } - - pub mod accessor { - use hook::result::HookResult; - use hook::accessor::MutableHookDataAccessor; - use hook::accessor::NonMutableHookDataAccessor; - use hook::accessor::StoreIdAccessor; - use hook::position::HookPosition; - use store::FileLockEntry; - use storeid::StoreId; - use hook::error::HookErrorKind as HEK; - use hook::error::CustomData; - use libimagerror::into::IntoError; - - #[derive(Debug)] - pub struct TestHookAccessor { - pos: HookPosition, - succeed: bool, - error_aborting: bool - } - - impl TestHookAccessor { - - pub fn new(position: HookPosition, succeed: bool, error_aborting: bool) - -> TestHookAccessor - { - TestHookAccessor { - pos: position, - succeed: succeed, - error_aborting: error_aborting, - } - } - - } - - fn get_result(succeed: bool, abort: bool) -> HookResult<()> { - println!("Generting result: succeed = {}, abort = {}", succeed, abort); - if succeed { - println!("Generating result: Ok(())"); - Ok(()) - } else { - if abort { - println!("Generating result: Err(_), aborting"); - Err(HEK::HookExecutionError.into_error()) - } else { - println!("Generating result: Err(_), not aborting"); - let custom = CustomData::default().aborting(false); - Err(HEK::HookExecutionError.into_error().with_custom_data(custom)) - } - } - } - - impl StoreIdAccessor for TestHookAccessor { - - fn access(&self, _: &StoreId) -> HookResult<()> { - get_result(self.succeed, self.error_aborting) - } - - } - - impl MutableHookDataAccessor for TestHookAccessor { - - fn access_mut(&self, _: &mut FileLockEntry) -> HookResult<()> { - get_result(self.succeed, self.error_aborting) - } - - } - - impl NonMutableHookDataAccessor for TestHookAccessor { - - fn access(&self, _: &FileLockEntry) -> HookResult<()> { - get_result(self.succeed, self.error_aborting) - } - - } - - } - - } - - use std::path::PathBuf; - - use hook::position::HookPosition as HP; - use storeid::StoreId; - use store::Store; - - use self::test_hook::TestHook; - - fn get_store_with_config() -> Store { - use toml::de::from_str; - - let cfg : ::toml::Value = from_str(mini_config()).unwrap(); - println!("Config parsed: {:?}", cfg); - Store::new(PathBuf::from("/"), Some(cfg.get("store").cloned().unwrap())).unwrap() - } - - fn mini_config() -> &'static str { - r#" -[store] -store-unload-hook-aspects = [ "test" ] -pre-create-hook-aspects = [ "test" ] -post-create-hook-aspects = [ "test" ] -pre-move-hook-aspects = [ "test" ] -post-move-hook-aspects = [ "test" ] -pre-retrieve-hook-aspects = [ "test" ] -post-retrieve-hook-aspects = [ "test" ] -pre-update-hook-aspects = [ "test" ] -post-update-hook-aspects = [ "test" ] -pre-delete-hook-aspects = [ "test" ] -post-delete-hook-aspects = [ "test" ] - -[store.aspects.test] -parallel = false -mutable_hooks = true - -[store.hooks.testhook_succeeding] -aspect = "test" - "# - } - - fn test_hook_execution(hook_positions: &[HP], storeid_name: &str) { - let mut store = get_store_with_config(); - - println!("Registering hooks..."); - for pos in hook_positions { - let hook = TestHook::new(pos.clone(), true, false); - println!("\tRegistering: {:?}", pos); - assert!(store.register_hook(pos.clone(), "test", Box::new(hook)) - .map_err(|e| println!("{:?}", e)) - .is_ok() - ); - } - println!("... done."); - - let pb = StoreId::new_baseless(PathBuf::from(storeid_name)).unwrap(); - let pb_moved = StoreId::new_baseless(PathBuf::from(format!("{}-moved", storeid_name))).unwrap(); - - println!("Creating {:?}", pb); - assert!(store.create(pb.clone()).is_ok()); - - { - println!("Getting {:?} -> Some?", pb); - assert!(match store.get(pb.clone()) { - Ok(Some(_)) => true, - _ => false, - }); - } - - { - println!("Getting {:?} -> None?", pb_moved); - assert!(match store.get(pb_moved.clone()) { - Ok(None) => true, - _ => false, - }); - } - - { - println!("Moving {:?} -> {:?}", pb, pb_moved); - assert!(store.move_by_id(pb.clone(), pb_moved.clone()).map_err(|e| println!("ERROR MOVING: {:?}", e)).is_ok()); - } - - { - println!("Getting {:?} -> None", pb); - assert!(match store.get(pb.clone()) { - Ok(None) => true, - _ => false, - }); - } - - { - println!("Getting {:?} -> Some", pb_moved); - assert!(match store.get(pb_moved.clone()) { - Ok(Some(_)) => true, - _ => false, - }); - } - - { - println!("Getting {:?} -> Some -> updating", pb_moved); - assert!(match store.get(pb_moved.clone()).map_err(|e| println!("ERROR GETTING: {:?}", e)) { - Ok(Some(mut fle)) => store.update(&mut fle) - .map_err(|e| println!("ERROR UPDATING: {:?}", e)).is_ok(), - _ => false, - }); - } - - println!("Deleting {:?}", pb_moved); - assert!(store.delete(pb_moved).is_ok()); - } - - #[test] - fn test_storeunload() { - test_hook_execution(&[HP::StoreUnload], "test_storeunload"); - } - - #[test] - fn test_precreate() { - test_hook_execution(&[HP::PreCreate], "test_precreate"); - } - - #[test] - fn test_postcreate() { - test_hook_execution(&[HP::PostCreate], "test_postcreate"); - } - - #[test] - fn test_preretrieve() { - test_hook_execution(&[HP::PreRetrieve], "test_preretrieve"); - } - - #[test] - fn test_postretrieve() { - test_hook_execution(&[HP::PostRetrieve], "test_postretrieve"); - } - - #[test] - fn test_preupdate() { - test_hook_execution(&[HP::PreUpdate], "test_preupdate"); - } - - #[test] - fn test_postupdate() { - test_hook_execution(&[HP::PostUpdate], "test_postupdate"); - } - - #[test] - fn test_predelete() { - test_hook_execution(&[HP::PreDelete], "test_predelete"); - } - - #[test] - fn test_postdelete() { - test_hook_execution(&[HP::PostDelete], "test_postdelete"); - } - - #[test] - fn test_multiple_same_position() { - let positions = [ HP::StoreUnload, HP::PreCreate, HP::PostCreate, HP::PreRetrieve, - HP::PostRetrieve, HP::PreUpdate, HP::PostUpdate, HP::PreDelete, HP::PostDelete ]; - - for position in positions.iter() { - for n in 2..10 { - let mut v = Vec::with_capacity(n); - for _ in 0..n { v.push(position.clone()); } - - test_hook_execution(&v, "test_multiple_same_position"); - } - } - } - - - fn get_store_with_aborting_hook_at_pos(pos: HP) -> Store { - let mut store = get_store_with_config(); - let hook = TestHook::new(pos.clone(), false, true); - - assert!(store.register_hook(pos, "test", Box::new(hook)).map_err(|e| println!("{:?}", e)).is_ok()); - store - } - - #[test] - fn test_pre_create_error() { - let storeid = StoreId::new_baseless(PathBuf::from("test_pre_create_error")).unwrap(); - let store = get_store_with_aborting_hook_at_pos(HP::PreCreate); - assert!(store.create(storeid).is_err()); - } - - #[test] - fn test_pre_retrieve_error() { - let storeid = StoreId::new_baseless(PathBuf::from("test_pre_retrieve_error")).unwrap(); - let store = get_store_with_aborting_hook_at_pos(HP::PreRetrieve); - assert!(store.retrieve(storeid).is_err()); - } - - #[test] - fn test_pre_delete_error() { - let storeid = StoreId::new_baseless(PathBuf::from("test_pre_delete_error")).unwrap(); - let store = get_store_with_aborting_hook_at_pos(HP::PreDelete); - assert!(store.delete(storeid).is_err()); - } - - #[test] - fn test_pre_update_error() { - let storeid = StoreId::new_baseless(PathBuf::from("test_pre_update_error")).unwrap(); - let store = get_store_with_aborting_hook_at_pos(HP::PreUpdate); - let mut fle = store.create(storeid).unwrap(); - - assert!(store.update(&mut fle).is_err()); - } - - #[test] - fn test_post_create_error() { - let store = get_store_with_aborting_hook_at_pos(HP::PostCreate); - let pb = StoreId::new_baseless(PathBuf::from("test_post_create_error")).unwrap(); - - assert!(store.create(pb.clone()).is_err()); - - // But the entry exists, as the hook fails post-create - assert!(store.entries.read().unwrap().get(&pb.with_base(store.path().clone())).is_some()); - } - - #[test] - fn test_post_retrieve_error() { - let store = get_store_with_aborting_hook_at_pos(HP::PostRetrieve); - let pb = StoreId::new_baseless(PathBuf::from("test_post_retrieve_error")).unwrap(); - - assert!(store.retrieve(pb.clone()).is_err()); - - // But the entry exists, as the hook fails post-retrieve - assert!(store.entries.read().unwrap().get(&pb.with_base(store.path().clone())).is_some()); - } - - #[test] - fn test_post_delete_error() { - let store = get_store_with_aborting_hook_at_pos(HP::PostDelete); - let pb = StoreId::new_baseless(PathBuf::from("test_post_delete_error")).unwrap(); - - assert!(store.create(pb.clone()).is_ok()); - let pb = pb.with_base(store.path().clone()); - assert!(store.entries.read().unwrap().get(&pb).is_some()); - - assert!(store.delete(pb.clone()).is_err()); - // But the entry is removed, as we fail post-delete - assert!(store.entries.read().unwrap().get(&pb).is_none()); - } - - #[test] - fn test_post_update_error() { - let store = get_store_with_aborting_hook_at_pos(HP::PostUpdate); - let pb = StoreId::new_baseless(PathBuf::from("test_post_update_error")).unwrap(); - let mut fle = store.create(pb.clone()).unwrap(); - let pb = pb.with_base(store.path().clone()); - - assert!(store.entries.read().unwrap().get(&pb).is_some()); - assert!(store.update(&mut fle).is_err()); - } - - fn get_store_with_allowed_error_hook_at_pos(pos: HP) -> Store { - let mut store = get_store_with_config(); - let hook = TestHook::new(pos.clone(), false, false); - - assert!(store.register_hook(pos, "test", Box::new(hook)).map_err(|e| println!("{:?}", e)).is_ok()); - store - } - - #[test] - fn test_pre_create_allowed_error() { - let storeid = StoreId::new_baseless(PathBuf::from("test_pre_create_allowed_error")).unwrap(); - let store = get_store_with_allowed_error_hook_at_pos(HP::PreCreate); - assert!(store.create(storeid).is_ok()); - } - - #[test] - fn test_pre_retrieve_allowed_error() { - let storeid = StoreId::new_baseless(PathBuf::from("test_pre_retrieve_allowed_error")).unwrap(); - let store = get_store_with_allowed_error_hook_at_pos(HP::PreRetrieve); - assert!(store.retrieve(storeid).is_ok()); - } - - #[test] - fn test_pre_delete_allowed_error() { - let storeid = StoreId::new_baseless(PathBuf::from("test_pre_delete_allowed_error")).unwrap(); - let store = get_store_with_allowed_error_hook_at_pos(HP::PreDelete); - assert!(store.retrieve(storeid.clone()).is_ok()); - assert!(store.delete(storeid).map_err(|e| println!("{:?}", e)).is_ok()); - } - - #[test] - fn test_pre_update_allowed_error() { - let storeid = StoreId::new_baseless(PathBuf::from("test_pre_update_allowed_error")).unwrap(); - let store = get_store_with_allowed_error_hook_at_pos(HP::PreUpdate); - let mut fle = store.create(storeid).unwrap(); - - assert!(store.update(&mut fle).is_ok()); - } - - #[test] - fn test_post_create_allowed_error() { - let store = get_store_with_allowed_error_hook_at_pos(HP::PostCreate); - let pb = StoreId::new_baseless(PathBuf::from("test_pre_create_allowed_error")).unwrap(); - - assert!(store.create(pb.clone()).is_ok()); - - // But the entry exists, as the hook fails post-create - assert!(store.entries.read().unwrap().get(&pb.with_base(store.path().clone())).is_some()); - } - - #[test] - fn test_post_retrieve_allowed_error() { - let store = get_store_with_allowed_error_hook_at_pos(HP::PostRetrieve); - let pb = StoreId::new_baseless(PathBuf::from("test_pre_retrieve_allowed_error")).unwrap(); - - assert!(store.retrieve(pb.clone()).is_ok()); - - // But the entry exists, as the hook fails post-retrieve - assert!(store.entries.read().unwrap().get(&pb.with_base(store.path().clone())).is_some()); - } - - #[test] - fn test_post_delete_allowed_error() { - let store = get_store_with_allowed_error_hook_at_pos(HP::PostDelete); - let pb = StoreId::new_baseless(PathBuf::from("test_post_delete_allowed_error")).unwrap(); - - assert!(store.create(pb.clone()).is_ok()); - let pb = pb.with_base(store.path().clone()); - assert!(store.entries.read().unwrap().get(&pb).is_some()); - - assert!(store.delete(pb.clone()).is_ok()); - // But the entry is removed, as we fail post-delete - assert!(store.entries.read().unwrap().get(&pb).is_none()); - } - - #[test] - fn test_post_update_allowed_error() { - let store = get_store_with_allowed_error_hook_at_pos(HP::PostUpdate); - let pb = StoreId::new_baseless(PathBuf::from("test_pre_update_allowed_error")).unwrap(); - let mut fle = store.create(pb.clone()).unwrap(); - let pb = pb.with_base(store.path().clone()); - - assert!(store.entries.read().unwrap().get(&pb).is_some()); - assert!(store.update(&mut fle).is_ok()); - } -}