Remove hook support from store

This commit is contained in:
Matthias Beyer 2017-06-04 16:30:43 +02:00
parent 1b8ccb42a7
commit 204ef24703
10 changed files with 7 additions and 1820 deletions

View file

@ -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 = <Boolean>" setting
/// * Whether each hook congfiguration has a "aspect = <String>" 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<Value>) -> 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<String, Value>, 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<F>(store_config: &BTreeMap<String, Value>,
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<Value>) -> Vec<String> {
get_aspect_names_for_aspect_position("store-unload-hook-aspects", value)
}
pub fn get_pre_create_aspect_names(value: &Option<Value>) -> Vec<String> {
get_aspect_names_for_aspect_position("pre-create-hook-aspects", value)
}
pub fn get_post_create_aspect_names(value: &Option<Value>) -> Vec<String> {
get_aspect_names_for_aspect_position("post-create-hook-aspects", value)
}
pub fn get_pre_retrieve_aspect_names(value: &Option<Value>) -> Vec<String> {
get_aspect_names_for_aspect_position("pre-retrieve-hook-aspects", value)
}
pub fn get_post_retrieve_aspect_names(value: &Option<Value>) -> Vec<String> {
get_aspect_names_for_aspect_position("post-retrieve-hook-aspects", value)
}
pub fn get_pre_update_aspect_names(value: &Option<Value>) -> Vec<String> {
get_aspect_names_for_aspect_position("pre-update-hook-aspects", value)
}
pub fn get_post_update_aspect_names(value: &Option<Value>) -> Vec<String> {
get_aspect_names_for_aspect_position("post-update-hook-aspects", value)
}
pub fn get_pre_delete_aspect_names(value: &Option<Value>) -> Vec<String> {
get_aspect_names_for_aspect_position("pre-delete-hook-aspects", value)
}
pub fn get_post_delete_aspect_names(value: &Option<Value>) -> Vec<String> {
get_aspect_names_for_aspect_position("post-delete-hook-aspects", value)
}
pub fn get_pre_move_aspect_names(value: &Option<Value>) -> Vec<String> {
get_aspect_names_for_aspect_position("pre-move-hook-aspects", value)
}
pub fn get_post_move_aspect_names(value: &Option<Value>) -> Vec<String> {
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][<aspect_name>]`.
///
/// Returns `None` if one of the keys in the chain is not available
pub fn get_for(v: &Option<Value>, a_name: String) -> Option<AspectConfig> {
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<Value>) -> Vec<String> {
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());
}
}

View file

@ -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",

View file

@ -1,49 +0,0 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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;
}

View file

@ -1,151 +0,0 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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<AspectConfig>,
name: String,
hooks: Vec<Box<Hook>>,
}
impl Aspect {
pub fn new(name: String, cfg: Option<AspectConfig>) -> Aspect {
Aspect {
cfg: cfg,
name: name,
hooks: vec![],
}
}
pub fn name(&self) -> &String {
&self.name
}
pub fn register_hook(&mut self, h: Box<Hook>) {
self.hooks.push(h);
}
}
impl StoreIdAccessor for Aspect {
fn access(&self, id: &StoreId) -> HookResult<()> {
let accessors : Vec<HDA> = 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<HDA> = 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<HDA> = 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)
}
})
}

View file

@ -1,65 +0,0 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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
}
}
}

View file

@ -1,36 +0,0 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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);
}

View file

@ -1,32 +0,0 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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,
}

View file

@ -1,22 +0,0 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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<T> = Result<T, HookError>;

View file

@ -52,7 +52,6 @@ extern crate libimagutil;
pub mod storeid;
pub mod error;
pub mod hook;
pub mod store;
mod configuration;
mod file_abstraction;

File diff suppressed because it is too large Load diff