Remove hook support from store
This commit is contained in:
parent
1b8ccb42a7
commit
204ef24703
10 changed files with 7 additions and 1820 deletions
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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,
|
||||
}
|
|
@ -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>;
|
|
@ -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
Loading…
Reference in a new issue