From 1b8ccb42a715e83ef95ade3fa2615a6cfdaf67a0 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 4 Jun 2017 16:26:38 +0200 Subject: [PATCH 01/23] libimagstorestdhook: Remove --- .imag-documentation/Cargo.toml | 3 - Cargo.toml | 1 - libimagstorestdhook/Cargo.toml | 36 --- libimagstorestdhook/README.md | 1 - libimagstorestdhook/src/debug.rs | 134 --------- libimagstorestdhook/src/denylinkeddelete.rs | 119 -------- libimagstorestdhook/src/flock.rs | 167 ----------- libimagstorestdhook/src/lib.rs | 52 ---- libimagstorestdhook/src/linkverify.rs | 93 ------ libimagstorestdhook/src/vcs/git/action.rs | 69 ----- libimagstorestdhook/src/vcs/git/config.rs | 241 ---------------- libimagstorestdhook/src/vcs/git/delete.rs | 242 ---------------- libimagstorestdhook/src/vcs/git/error.rs | 97 ------- libimagstorestdhook/src/vcs/git/mod.rs | 29 -- libimagstorestdhook/src/vcs/git/result.rs | 24 -- libimagstorestdhook/src/vcs/git/runtime.rs | 178 ------------ .../src/vcs/git/store_unload.rs | 264 ------------------ libimagstorestdhook/src/vcs/git/update.rs | 263 ----------------- libimagstorestdhook/src/vcs/git/util.rs | 42 --- libimagstorestdhook/src/vcs/mod.rs | 20 -- 20 files changed, 2075 deletions(-) delete mode 100644 libimagstorestdhook/Cargo.toml delete mode 120000 libimagstorestdhook/README.md delete mode 100644 libimagstorestdhook/src/debug.rs delete mode 100644 libimagstorestdhook/src/denylinkeddelete.rs delete mode 100644 libimagstorestdhook/src/flock.rs delete mode 100644 libimagstorestdhook/src/lib.rs delete mode 100644 libimagstorestdhook/src/linkverify.rs delete mode 100644 libimagstorestdhook/src/vcs/git/action.rs delete mode 100644 libimagstorestdhook/src/vcs/git/config.rs delete mode 100644 libimagstorestdhook/src/vcs/git/delete.rs delete mode 100644 libimagstorestdhook/src/vcs/git/error.rs delete mode 100644 libimagstorestdhook/src/vcs/git/mod.rs delete mode 100644 libimagstorestdhook/src/vcs/git/result.rs delete mode 100644 libimagstorestdhook/src/vcs/git/runtime.rs delete mode 100644 libimagstorestdhook/src/vcs/git/store_unload.rs delete mode 100644 libimagstorestdhook/src/vcs/git/update.rs delete mode 100644 libimagstorestdhook/src/vcs/git/util.rs delete mode 100644 libimagstorestdhook/src/vcs/mod.rs diff --git a/.imag-documentation/Cargo.toml b/.imag-documentation/Cargo.toml index 307987ea..bbe8bf58 100644 --- a/.imag-documentation/Cargo.toml +++ b/.imag-documentation/Cargo.toml @@ -60,9 +60,6 @@ path = "../libimagrt" [dependencies.libimagstore] path = "../libimagstore" -[dependencies.libimagstorestdhook] -path = "../libimagstorestdhook" - [dependencies.libimagtimeui] path = "../libimagtimeui" diff --git a/Cargo.toml b/Cargo.toml index c5212cda..56298a5c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,6 @@ members = [ "libimagref", "libimagrt", "libimagstore", - "libimagstorestdhook", "libimagtimeui", "libimagtodo", "libimagutil", diff --git a/libimagstorestdhook/Cargo.toml b/libimagstorestdhook/Cargo.toml deleted file mode 100644 index 3e70f61a..00000000 --- a/libimagstorestdhook/Cargo.toml +++ /dev/null @@ -1,36 +0,0 @@ -[package] -name = "libimagstorestdhook" -version = "0.3.0" -authors = ["Matthias Beyer "] - -description = "Library for the imag core distribution" - -keywords = ["imag", "PIM", "personal", "information", "management"] -readme = "../README.md" -license = "LGPL-2.1" - -documentation = "https://matthiasbeyer.github.io/imag/imag_documentation/index.html" -repository = "https://github.com/matthiasbeyer/imag" -homepage = "http://imag-pim.org" - -[dependencies] -toml = "^0.4" -log = "0.3" -fs2 = "0.3" -git2 = "0.5" - -[dependencies.libimagstore] -path = "../libimagstore" - -[dependencies.libimagentrylink] -path = "../libimagentrylink" - -[dependencies.libimaginteraction] -path = "../libimaginteraction" - -[dependencies.libimagerror] -path = "../libimagerror" - -[dependencies.libimagutil] -path = "../libimagutil" - diff --git a/libimagstorestdhook/README.md b/libimagstorestdhook/README.md deleted file mode 120000 index 495accc7..00000000 --- a/libimagstorestdhook/README.md +++ /dev/null @@ -1 +0,0 @@ -../doc/src/05100-lib-store-std-hook.md \ No newline at end of file diff --git a/libimagstorestdhook/src/debug.rs b/libimagstorestdhook/src/debug.rs deleted file mode 100644 index af20f330..00000000 --- a/libimagstorestdhook/src/debug.rs +++ /dev/null @@ -1,134 +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 toml::Value; - -use libimagstore::hook::Hook; -use libimagstore::hook::accessor::HookDataAccessor; -use libimagstore::hook::accessor::HookDataAccessorProvider; -use libimagstore::hook::position::HookPosition; - -use self::accessor::DebugHookAccessor as DHA; - -#[derive(Debug)] -pub struct DebugHook { - position: HookPosition, - accessor: DHA, -} - -impl DebugHook { - - pub fn new(pos: HookPosition) -> DebugHook { - DebugHook { - position: pos.clone(), - accessor: DHA::new(pos), - } - } - -} - -impl Hook for DebugHook { - - fn name(&self) -> &'static str { - "stdhook_debug" - } - - fn set_config(&mut self, c: &Value) { - debug!("Trying to set configuration in debug hook: {:?}", c); - debug!("Ignoring configuration in debug hook, we don't need a config here"); - } - -} - -impl HookDataAccessorProvider for DebugHook { - - fn accessor(&self) -> HookDataAccessor { - use libimagstore::hook::position::HookPosition as HP; - use libimagstore::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 std::ops::Deref; - - use libimagstore::storeid::StoreId; - use libimagstore::store::FileLockEntry; - use libimagstore::hook::result::HookResult; - use libimagstore::hook::accessor::MutableHookDataAccessor; - use libimagstore::hook::accessor::NonMutableHookDataAccessor; - use libimagstore::hook::accessor::StoreIdAccessor; - use libimagstore::hook::position::HookPosition; - - #[derive(Debug)] - pub struct DebugHookAccessor { - position: HookPosition, - } - - impl DebugHookAccessor { - - pub fn new(position: HookPosition) -> DebugHookAccessor { - DebugHookAccessor { - position: position, - } - } - - } - - impl StoreIdAccessor for DebugHookAccessor { - - fn access(&self, id: &StoreId) -> HookResult<()> { - debug!("[DEBUG HOOK]: {:?}", id); - Ok(()) - } - - } - - impl MutableHookDataAccessor for DebugHookAccessor { - - fn access_mut(&self, fle: &mut FileLockEntry) -> HookResult<()> { - debug!("[DEBUG HOOK] {:?}", fle.deref().deref()); - Ok(()) - } - - } - - impl NonMutableHookDataAccessor for DebugHookAccessor { - - fn access(&self, fle: &FileLockEntry) -> HookResult<()> { - debug!("[DEBUG HOOK] {:?}", fle.deref().deref()); - Ok(()) - } - - } - -} - diff --git a/libimagstorestdhook/src/denylinkeddelete.rs b/libimagstorestdhook/src/denylinkeddelete.rs deleted file mode 100644 index 6186535a..00000000 --- a/libimagstorestdhook/src/denylinkeddelete.rs +++ /dev/null @@ -1,119 +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 toml::Value; - -use libimagstore::hook::Hook; -use libimagstore::hook::error::HookErrorKind as HEK; -use libimagstore::hook::accessor::HookDataAccessor as HDA; -use libimagstore::hook::accessor::HookDataAccessorProvider; -use libimagstore::hook::accessor::NonMutableHookDataAccessor; -use libimagstore::hook::result::HookResult; -use libimagstore::store::FileLockEntry; -use libimagstore::toml_ext::TomlValueExt; -use libimagentrylink::internal::InternalLinker; -use libimagerror::trace::trace_error; - -mod error { - generate_error_imports!(); - generate_error_types!(NoLinksLeftCheckerHookError, NoLinksLeftCheckerHookErrorKind, - ReadInternalLinksError => "Error while reading internal links of entry", - LinksLeft => "The entry has links and therefor cannot be deleted." - ); -} -use self::error::NoLinksLeftCheckerHookErrorKind as NLLCHEK; - -#[derive(Debug, Clone)] -pub struct DenyDeletionOfLinkedEntriesHook { - abort: bool -} - -impl DenyDeletionOfLinkedEntriesHook { - - pub fn new() -> DenyDeletionOfLinkedEntriesHook { - DenyDeletionOfLinkedEntriesHook { - abort: true // by default, this hook aborts actions - } - } - -} - -impl Hook for DenyDeletionOfLinkedEntriesHook { - - fn name(&self) -> &'static str { - "stdhook_linked_entries_cannot_be_deleted" - } - - fn set_config(&mut self, v: &Value) { - self.abort = match v.read("aborting") { - Ok(Some(Value::Boolean(b))) => b, - Ok(Some(_)) => { - warn!("Configuration error, 'aborting' must be a Boolean (true|false)."); - warn!("Assuming 'true' now."); - true - }, - Ok(None) => { - warn!("No key 'aborting' - Assuming 'true'"); - true - }, - Err(e) => { - error!("Error parsing TOML:"); - trace_error(&e); - false - }, - }; - } - -} - -impl HookDataAccessorProvider for DenyDeletionOfLinkedEntriesHook { - - fn accessor(&self) -> HDA { - HDA::NonMutableAccess(self) - } - -} - -impl NonMutableHookDataAccessor for DenyDeletionOfLinkedEntriesHook { - - fn access(&self, fle: &FileLockEntry) -> HookResult<()> { - use libimagerror::into::IntoError; - use self::error::MapErrInto; - - debug!("[NO LINKS LEFT CHECKER HOOK] {:?}", fle.get_location()); - - let n = try!(fle - .get_internal_links() - .map(|i| i.count()) - .map_err_into(NLLCHEK::ReadInternalLinksError) - .map_err(Box::new) - .map_err(|e| HEK::HookExecutionError.into_error_with_cause(e))); - - if n > 0 { - Err(NLLCHEK::LinksLeft.into_error()) - .map_err(Box::new) - .map_err(|e| HEK::HookExecutionError.into_error_with_cause(e)) - } else { - Ok(()) - } - } - -} - - diff --git a/libimagstorestdhook/src/flock.rs b/libimagstorestdhook/src/flock.rs deleted file mode 100644 index 6cc30864..00000000 --- a/libimagstorestdhook/src/flock.rs +++ /dev/null @@ -1,167 +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 toml::Value; - -use fs2::FileExt; -use std::fs::File; - -use libimagstore::hook::Hook; -use libimagstore::hook::accessor::HookDataAccessor as HDA; -use libimagstore::hook::accessor::HookDataAccessorProvider; -use libimagstore::hook::accessor::StoreIdAccessor; -use libimagstore::hook::accessor::MutableHookDataAccessor; -use libimagstore::hook::accessor::NonMutableHookDataAccessor; -use libimagstore::hook::result::HookResult; -use libimagstore::hook::error::{HookError, HookErrorKind}; -use libimagstore::storeid::StoreId; -use libimagstore::store::FileLockEntry; -use libimagstore::store::Entry; - -mod error { - generate_error_imports!(); - generate_error_types!(FlockError, FlockErrorKind, - IOError => "IO Error", - StoreIdPathBufConvertError => "Error while converting StoreId to PathBuf", - FileOpenError => "Error on File::open()", - LockError => "Error while lock()ing", - UnlockError => "Error while unlock()ing" - ); -} -use self::error::FlockError as FE; -use self::error::FlockErrorKind as FEK; -use self::error::MapErrInto; - -trait EntryFlock { - fn lock(&self) -> Result<(), FE>; - fn unlock(&self) -> Result<(), FE>; -} - -fn open_file(id: StoreId) -> Result { - id.into_pathbuf() - .map_err_into(FEK::StoreIdPathBufConvertError) - .and_then(|loc| { - File::open(loc) - .map_err_into(FEK::FileOpenError) - .map_err_into(FEK::IOError) - }) -} - -impl EntryFlock for Entry { - - fn lock(&self) -> Result<(), FE> { - open_file(self.get_location().clone()) - .and_then(|file| { - file.lock_exclusive() - .map_err_into(FEK::LockError) - .map_err_into(FEK::IOError) - }) - } - - fn unlock(&self) -> Result<(), FE> { - open_file(self.get_location().clone()) - .and_then(|file| { - file.unlock() - .map_err_into(FEK::UnlockError) - .map_err_into(FEK::LockError) - .map_err_into(FEK::IOError) - }) - } - -} - -#[derive(PartialEq, Eq, Debug, Clone)] -pub enum Action { - Lock, - Unlock -} - -fn action_to_str(a: &Action) -> &'static str { - match *a { - Action::Lock => "lock", - Action::Unlock => "unlock", - } -} - -#[derive(Debug, Clone)] -pub struct FlockUpdateHook { - action: Action, -} - -impl FlockUpdateHook { - - pub fn new(action: Action) -> FlockUpdateHook { - FlockUpdateHook { - action: action, - } - } - -} - -impl Hook for FlockUpdateHook { - - fn name(&self) -> &'static str { - "stdhook_flock_update" - } - - fn set_config(&mut self, _: &Value) { - () // We are not configurable here. - } - -} - -impl HookDataAccessorProvider for FlockUpdateHook { - - fn accessor(&self) -> HDA { - HDA::StoreIdAccess(self) - } - -} - -impl StoreIdAccessor for FlockUpdateHook { - - fn access(&self, id: &StoreId) -> HookResult<()> { - debug!("[FLOCK HOOK][{}] {:?}", action_to_str(&self.action), id); - Ok(()) - } - -} - -impl MutableHookDataAccessor for FlockUpdateHook { - - fn access_mut(&self, fle: &mut FileLockEntry) -> HookResult<()> { - debug!("[FLOCK HOOK][{}] {:?}", action_to_str(&self.action), fle.get_location()); - fle.lock() - .map_err(|e| HookError::new(HookErrorKind::HookExecutionError, Some(Box::new(e)))) - .map(|_| ()) - } - -} - -impl NonMutableHookDataAccessor for FlockUpdateHook { - - fn access(&self, fle: &FileLockEntry) -> HookResult<()> { - debug!("[FLOCK HOOK][{}] {:?}", action_to_str(&self.action), fle.get_location()); - fle.unlock() - .map_err(|e| HookError::new(HookErrorKind::HookExecutionError, Some(Box::new(e)))) - .map(|_| ()) - } - -} - diff --git a/libimagstorestdhook/src/lib.rs b/libimagstorestdhook/src/lib.rs deleted file mode 100644 index abfea88b..00000000 --- a/libimagstorestdhook/src/lib.rs +++ /dev/null @@ -1,52 +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 -// - -#![deny( - dead_code, - non_camel_case_types, - non_snake_case, - path_statements, - trivial_numeric_casts, - unstable_features, - unused_allocation, - unused_import_braces, - unused_imports, - unused_must_use, - unused_mut, - unused_qualifications, - while_true, -)] - -#[macro_use] extern crate log; -extern crate toml; -extern crate fs2; -extern crate git2; - -extern crate libimagstore; -extern crate libimagentrylink; -extern crate libimaginteraction; -#[macro_use] extern crate libimagerror; -extern crate libimagutil; - -pub mod debug; -pub mod denylinkeddelete; -pub mod flock; -pub mod linkverify; -pub mod vcs; - diff --git a/libimagstorestdhook/src/linkverify.rs b/libimagstorestdhook/src/linkverify.rs deleted file mode 100644 index 79194797..00000000 --- a/libimagstorestdhook/src/linkverify.rs +++ /dev/null @@ -1,93 +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::path::PathBuf; - -use toml::Value; - -use libimagstore::hook::Hook; -use libimagstore::hook::accessor::HookDataAccessor as HDA; -use libimagstore::hook::accessor::HookDataAccessorProvider; -use libimagstore::hook::accessor::NonMutableHookDataAccessor; -use libimagstore::hook::result::HookResult; -use libimagstore::store::FileLockEntry; -use libimagentrylink::internal::InternalLinker; -use libimagerror::trace::trace_error; - -#[derive(Debug, Clone)] -pub struct LinkedEntriesExistHook { - store_location: PathBuf, -} - -impl LinkedEntriesExistHook { - - pub fn new(store_location: PathBuf) -> LinkedEntriesExistHook { - LinkedEntriesExistHook { - store_location: store_location, - } - } - -} - -impl Hook for LinkedEntriesExistHook { - - fn name(&self) -> &'static str { - "stdhook_linked_entries_exist" - } - - fn set_config(&mut self, _: &Value) { - () // We are not configurable here. - } - -} - -impl HookDataAccessorProvider for LinkedEntriesExistHook { - - fn accessor(&self) -> HDA { - HDA::NonMutableAccess(self) - } - -} - -impl NonMutableHookDataAccessor for LinkedEntriesExistHook { - - fn access(&self, fle: &FileLockEntry) -> HookResult<()> { - use libimagstore::hook::error::HookErrorKind; - use libimagstore::hook::error::MapErrInto; - - debug!("[LINKVERIFY HOOK] {:?}", fle.get_location()); - match fle.get_internal_links() { - Ok(links) => { - for link in links { - if !try!(link.exists().map_err_into(HookErrorKind::HookExecutionError)) { - warn!("File link does not exist: {:?} -> {:?}", fle.get_location(), link); - } - } - Ok(()) - }, - Err(e) => { - warn!("Couldn't execute Link-Verify hook"); - trace_error(&e); - Err(e).map_err_into(HookErrorKind::HookExecutionError) - } - } - } - -} - diff --git a/libimagstorestdhook/src/vcs/git/action.rs b/libimagstorestdhook/src/vcs/git/action.rs deleted file mode 100644 index 76715c83..00000000 --- a/libimagstorestdhook/src/vcs/git/action.rs +++ /dev/null @@ -1,69 +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::{Display, Formatter, Error}; - -/// Utility type to specify which kind of store action is running -#[derive(Clone, Debug)] -pub enum StoreAction { - Create, - Retrieve, - Update, - Delete, - StoreUnload, -} - -impl StoreAction { - - pub fn uppercase(&self) -> &str { - match *self { - StoreAction::Create => "CREATE", - StoreAction::Retrieve => "RETRIEVE", - StoreAction::Update => "UPDATE", - StoreAction::Delete => "DELETE", - StoreAction::StoreUnload => "STORE UNLOAD", - } - } - - pub fn as_commit_message(&self) -> &str { - match *self { - StoreAction::Create => "Create", - StoreAction::Retrieve => "Retrieve", - StoreAction::Update => "Update", - StoreAction::Delete => "Delete", - StoreAction::StoreUnload => "Store Unload", - } - } -} - -impl Display for StoreAction { - - fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { - write!(fmt, "StoreAction: {}", - match *self { - StoreAction::Create => "create", - StoreAction::Retrieve => "retrieve", - StoreAction::Update => "update", - StoreAction::Delete => "delete", - StoreAction::StoreUnload => "store unload", - }) - } - -} - diff --git a/libimagstorestdhook/src/vcs/git/config.rs b/libimagstorestdhook/src/vcs/git/config.rs deleted file mode 100644 index 4c44db93..00000000 --- a/libimagstorestdhook/src/vcs/git/config.rs +++ /dev/null @@ -1,241 +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 toml::Value; - -use libimagerror::into::IntoError; -use libimagerror::trace::trace_error; -use libimagstore::storeid::StoreId; -use libimagstore::toml_ext::TomlValueExt; - -use vcs::git::error::GitHookErrorKind as GHEK; -use vcs::git::error::MapErrInto; -use vcs::git::result::Result; - -use vcs::git::action::StoreAction; - -use git2::Repository; - -/// Check the configuration whether we should commit interactively -pub fn commit_interactive(config: &Value, action: &StoreAction) -> bool { - match config.read("commit.interactive") { - Ok(Some(Value::Boolean(b))) => b, - Ok(Some(_)) => { - warn!("Configuration error, 'store.hooks.stdhook_git_{}.commit.interactive' must be a Boolean.", - action); - warn!("Defaulting to commit.interactive = false"); - false - } - Ok(None) => { - warn!("Unavailable configuration for"); - warn!("\t'store.hooks.stdhook_git_{}.commit.interactive'", action); - warn!("Defaulting to false"); - false - }, - Err(e) => { - error!("Error parsing TOML:"); - trace_error(&e); - false - }, - } -} - -/// Check the configuration whether we should commit with the editor -fn commit_with_editor(config: &Value, action: &StoreAction) -> bool { - match config.read("commit.interactive_editor") { - Ok(Some(Value::Boolean(b))) => b, - Ok(Some(_)) => { - warn!("Configuration error, 'store.hooks.stdhook_git_{}.commit.interactive_editor' must be a Boolean.", - action); - warn!("Defaulting to commit.interactive_editor = false"); - false - } - Ok(None) => { - warn!("Unavailable configuration for"); - warn!("\t'store.hooks.stdhook_git_{}.commit.interactive_editor'", action); - warn!("Defaulting to false"); - false - }, - Err(e) => { - error!("Error parsing TOML:"); - trace_error(&e); - false - }, - } -} - -/// Get the commit default message -fn commit_default_msg<'a>(config: &'a Value, action: &'a StoreAction) -> Result { - config.read("commit.message") - .map(|m| match m { - Some(Value::String(b)) => String::from(b), - Some(_) => { - warn!("Configuration error, 'store.hooks.stdhook_git_{}.commit.message' must be a String.", - action); - warn!("Defaulting to commit.message = '{}'", action.as_commit_message()); - String::from(action.as_commit_message()) - }, - None => { - warn!("Unavailable configuration for"); - warn!("\t'store.hooks.stdhook_git_{}.commit.message'", action); - warn!("Defaulting to commit.message = '{}'", action.as_commit_message()); - String::from(action.as_commit_message()) - }, - }) - .map_err_into(GHEK::ConfigError) - -} - -/// Get the commit template -/// -/// TODO: Implement good template string -fn commit_template(action: &StoreAction, id: &StoreId) -> String { - format!(r#" -# Please commit your changes and remove these lines. -# -# You're about to commit changes via the {action} Hook -# -# Altered file: {id} -# - "#, - action = action, - id = id.local().display()) -} - -/// Generate a commit message -/// -/// Uses the functions `commit_interactive()` and `commit_with_editor()` -/// or reads one from the commandline or uses the `commit_default_msg()` string to create a commit -/// message. -pub fn commit_message(repo: &Repository, config: &Value, action: StoreAction, id: &StoreId) -> Result { - use libimaginteraction::ask::ask_string; - use libimagutil::edit::edit_in_tmpfile_with_command; - use std::process::Command; - - if commit_interactive(config, &action) { - if commit_with_editor(config, &action) { - repo.config() - .map_err_into(GHEK::GitConfigFetchError) - .and_then(|c| c.get_string("core.editor").map_err_into(GHEK::GitConfigEditorFetchError)) - .map_err_into(GHEK::ConfigError) - .map(Command::new) - .and_then(|cmd| { - let mut s = commit_template(&action, id); - edit_in_tmpfile_with_command(cmd, &mut s).map(|_| s) - .map_err_into(GHEK::EditorError) - }) - } else { - Ok(ask_string("Commit Message", None, false, false, None, "> ")) - } - } else { - commit_default_msg(config, &action) - } -} - -/// Check whether the hook should abort if the repository cannot be initialized -pub fn abort_on_repo_init_err(cfg: &Value) -> bool { - get_bool_cfg(Some(cfg), "abort_on_repo_init_failure", true, true) -} - -/// Get the branch which must be checked out before running the hook (if any). -/// -/// If there is no configuration for this, this is `Ok(None)`, otherwise we try to find the -/// configuration `String`. -pub fn ensure_branch(cfg: Option<&Value>) -> Result> { - match cfg { - Some(cfg) => { - cfg.read("ensure_branch") - .map_err_into(GHEK::ConfigError) - .and_then(|toml| match toml { - Some(Value::String(ref s)) => Ok(Some(s.clone())), - Some(_) => { - warn!("Configuration error, 'ensure_branch' must be a String."); - Err(GHEK::ConfigTypeError.into_error()) - .map_err_into(GHEK::ConfigTypeError) - }, - None => { - debug!("No key `ensure_branch'"); - Ok(None) - }, - }) - }, - None => Ok(None), - } -} - -/// Check whether we should check out a branch before committing. -pub fn do_checkout_ensure_branch(cfg: Option<&Value>) -> bool { - get_bool_cfg(cfg, "try_checkout_ensure_branch", true, true) -} - -/// Helper to get a boolean value from the configuration. -fn get_bool_cfg(cfg: Option<&Value>, name: &str, on_fail: bool, on_unavail: bool) -> bool { - cfg.map(|cfg| { - match cfg.read(name) { - Ok(Some(Value::Boolean(b))) => b, - Ok(Some(_)) => { - warn!("Configuration error, '{}' must be a Boolean (true|false).", name); - warn!("Assuming '{}' now.", on_fail); - on_fail - }, - Ok(None) => { - warn!("No key '{}' - Assuming '{}'", name, on_unavail); - on_unavail - }, - Err(e) => { - error!("Error parsing TOML:"); - trace_error(&e); - false - }, - } - }) - .unwrap_or_else(|| { - warn!("No configuration to fetch {} from, assuming {}", name, on_unavail); - on_unavail - }) -} - -/// Check whether the hook is enabled or not. If the config is not there, the hook is _enabled_ by -/// default. -pub fn is_enabled(cfg: &Value) -> bool { - get_bool_cfg(Some(cfg), "enabled", true, true) -} - -/// Check whether committing is enabled for a hook. -pub fn committing_is_enabled(cfg: &Value) -> Result { - cfg.read("commit.enabled") - .map_err_into(GHEK::ConfigError) - .and_then(|toml| match toml { - Some(Value::Boolean(b)) => Ok(b), - Some(_) => { - warn!("Config setting whether committing is enabled or not has wrong type."); - warn!("Expected Boolean"); - Err(GHEK::ConfigTypeError.into_error()) - }, - None => { - warn!("No config setting whether committing is enabled or not."); - Err(GHEK::NoConfigError.into_error()) - }, - }) -} - -pub fn add_wt_changes_before_committing(cfg: &Value) -> bool { - get_bool_cfg(Some(cfg), "commit.add_wt_changes", true, true) -} - diff --git a/libimagstorestdhook/src/vcs/git/delete.rs b/libimagstorestdhook/src/vcs/git/delete.rs deleted file mode 100644 index 9886d252..00000000 --- a/libimagstorestdhook/src/vcs/git/delete.rs +++ /dev/null @@ -1,242 +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::path::PathBuf; -use std::path::Path; -use std::fmt::{Debug, Formatter, Error as FmtError}; -use std::result::Result as RResult; - -use toml::Value; - -use libimagerror::trace::trace_error; -use libimagstore::storeid::StoreId; -use libimagstore::hook::Hook; -use libimagstore::hook::result::HookResult; -use libimagstore::hook::position::HookPosition; -use libimagstore::hook::accessor::{HookDataAccessor, HookDataAccessorProvider}; -use libimagstore::hook::accessor::StoreIdAccessor; -use libimagutil::debug_result::*; - -use vcs::git::error::GitHookErrorKind as GHEK; -use vcs::git::error::MapErrInto; -use vcs::git::runtime::Runtime as GRuntime; - -pub struct DeleteHook { - storepath: PathBuf, - - runtime: GRuntime, - - position: HookPosition, -} - -impl DeleteHook { - - pub fn new(storepath: PathBuf, p: HookPosition) -> DeleteHook { - DeleteHook { - runtime: GRuntime::new(&storepath), - storepath: storepath, - position: p, - } - } - -} - -impl Debug for DeleteHook { - fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> { - write!(fmt, "DeleteHook(storepath={:?}, repository={}, pos={:?}, cfg={:?})", - self.storepath, - (if self.runtime.has_repository() { "Some(_)" } else { "None" }), - self.position, - self.runtime.has_config()) - } -} - - -impl Hook for DeleteHook { - - fn name(&self) -> &'static str { - "stdhook_git_delete" - } - - /// Set the configuration of the hook. See - /// `libimagstorestdhook::vcs::git::runtime::Runtime::set_config()`. - /// - /// This function traces the error (using `trace_error()`) that - /// `libimagstorestdhook::vcs::git::runtime::Runtime::set_config()` - /// returns, if any. - fn set_config(&mut self, config: &Value) { - if let Err(e) = self.runtime.set_config(config) { - trace_error(&e); - } - } - -} - -impl HookDataAccessorProvider for DeleteHook { - - fn accessor(&self) -> HookDataAccessor { - HookDataAccessor::StoreIdAccess(self) - } -} - -impl StoreIdAccessor for DeleteHook { - - fn access(&self, id: &StoreId) -> HookResult<()> { - use libimagerror::into::IntoError; - use vcs::git::action::StoreAction; - use vcs::git::config::commit_message; - use vcs::git::error::MapIntoHookError; - use vcs::git::util::fetch_index; - use vcs::git::config::abort_on_repo_init_err; - use vcs::git::config::is_enabled; - use vcs::git::config::committing_is_enabled; - use git2::{ADD_DEFAULT, STATUS_WT_DELETED, IndexMatchedPath}; - - debug!("[GIT DELETE HOOK]: {:?}", id); - - let action = StoreAction::Delete; - let cfg = try!(self.runtime.config_value_or_err(&action)); - - if !is_enabled(cfg) { - return Ok(()) - } - - if !self.runtime.has_repository() { - debug!("[GIT DELETE HOOK]: Runtime has no repository..."); - if try!(self.runtime.config_value_or_err(&action).map(|c| abort_on_repo_init_err(c))) { - // Abort on repo init failure - debug!("[GIT DELETE HOOK]: Config says we should abort if we have no repository"); - debug!("[GIT DELETE HOOK]: Returing Err(_)"); - return Err(GHEK::RepositoryInitError.into_error()) - .map_err_into(GHEK::RepositoryError) - .map_into_hook_error() - } else { - debug!("[GIT DELETE HOOK]: Config says it is okay to not have a repository"); - debug!("[GIT DELETE HOOK]: Returing Ok(())"); - return Ok(()) - } - } - - let _ = try!(self.runtime.ensure_cfg_branch_is_checked_out(&action)); - let repo = try!(self.runtime.repository(&action)); - let mut index = try!(fetch_index(repo, &action)); - - let signature = try!( - repo.signature() - .map_err_into(GHEK::MkSignature) - .map_dbg_err_str("Failed to fetch signature") - .map_dbg_str("[GIT DELETE HOOK]: Fetched signature object") - .map_into_hook_error() - ); - - let head = try!( - repo.head() - .map_err_into(GHEK::HeadFetchError) - .map_dbg_err_str("Failed to fetch HEAD") - .map_dbg_str("[GIT DELETE HOOK]: Fetched HEAD") - .map_into_hook_error() - ); - - let file_status = try!( - repo - .status_file(id.local()) - .map_dbg_err_str("Failed to fetch file status") - .map_dbg_err(|e| format!("\t-> {:?}", e)) - .map_dbg_str("[GIT DELETE HOOK]: Fetched file status") - .map_err_into(GHEK::RepositoryFileStatusError) - .map_into_hook_error() - ); - - let cb = &mut |path: &Path, _matched_spec: &[u8]| -> i32 { - debug!("[GIT DELETE HOOK]: Checking file status for: {}", path.display()); - if file_status.contains(STATUS_WT_DELETED) { - debug!("[GIT DELETE HOOK]: File is deleted: {}", path.display()); - 0 - } else { - debug!("[GIT DELETE HOOK]: Ignoring file: {}", path.display()); - 1 - } - }; - - try!( - index.add_all(&[id.local()], ADD_DEFAULT, Some(cb as &mut IndexMatchedPath)) - .map_err_into(GHEK::RepositoryPathAddingError) - .map_dbg_err_str("Failed to add to index") - .map_dbg_str("[GIT DELETE HOOK]: Fetched index") - .map_into_hook_error() - ); - - let tree_id = try!( - index.write_tree() - .map_err_into(GHEK::RepositoryIndexWritingError) - .map_dbg_err_str("Failed to write tree") - .map_dbg_str("[GIT DELETE HOOK]: Wrote index tree") - .map_into_hook_error() - ); - - if !try!(committing_is_enabled(cfg)) { - debug!("Committing not enabled. This is fine, returning now..."); - return Ok(()) - } - - let mut parents = Vec::new(); - { - let commit = try!( - repo.find_commit(head.target().unwrap()) - .map_err_into(GHEK::RepositoryParentFetchingError) - .map_dbg_err_str("Failed to find commit HEAD") - .map_dbg_str("[GIT DELETE HOOK]: Found commit HEAD") - .map_into_hook_error() - ); - parents.push(commit); - } - - // for converting from Vec to Vec<&Commit> - let parents = parents.iter().collect::>(); - - let tree = try!( - repo.find_tree(tree_id) - .map_err_into(GHEK::RepositoryParentFetchingError) - .map_dbg_err_str("Failed to find tree") - .map_dbg_str("[GIT DELETE HOOK]: Found tree for index") - .map_into_hook_error() - ); - - let message = try!(commit_message(&repo, cfg, action, &id) - .map_dbg_err_str("Failed to get commit message") - .map_dbg_str("[GIT DELETE HOOK]: Got commit message")); - - try!(repo.commit(Some("HEAD"), &signature, &signature, &message, &tree, &parents) - .map_dbg_str("Committed") - .map_dbg_err_str("Failed to commit") - .map_dbg_str("[GIT DELETE HOOK]: Committed") - .map_err_into(GHEK::RepositoryCommittingError) - .map_into_hook_error() - ); - - index.write() - .map_err_into(GHEK::RepositoryIndexWritingError) - .map_dbg_err_str("Failed to write tree") - .map_dbg_str("[GIT DELETE HOOK]: Wrote index") - .map_into_hook_error() - .map(|_| ()) - } - -} - diff --git a/libimagstorestdhook/src/vcs/git/error.rs b/libimagstorestdhook/src/vcs/git/error.rs deleted file mode 100644 index 34ae0439..00000000 --- a/libimagstorestdhook/src/vcs/git/error.rs +++ /dev/null @@ -1,97 +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 git2::Error as Git2Error; - -use libimagstore::hook::error::HookError as HE; -use libimagstore::hook::error::HookErrorKind as HEK; -use libimagstore::hook::result::HookResult; - -generate_error_module!( - generate_error_types!(GitHookError, GitHookErrorKind, - ConfigError => "Configuration Error", - NoConfigError => "No Configuration", - ConfigTypeError => "Configuration value type wrong", - - RepositoryError => "Error while interacting with git repository", - RepositoryInitError => "Error while loading the git repository", - RepositoryBackendError => "Error in the git library", - RepositoryBranchError => "Error while interacting with git branch(es)", - RepositoryBranchNameFetchingError => "Error while fetching branch name", - RepositoryWrongBranchError => "Error because repository is on wrong branch", - RepositoryIndexFetchingError => "Error while fetching Repository Index", - RepositoryIndexWritingError => "Error while writing Repository Index", - RepositoryPathAddingError => "Error while adding Path to Index", - RepositoryCommittingError => "Error while committing", - RepositoryParentFetchingError => "Error while fetching parent of commit", - RepositoryStatusFetchError => "Error while fetching repository status", - - HeadFetchError => "Error while getting HEAD", - NotOnBranch => "No Branch is checked out", - MkRepo => "Repository creation error", - MkSignature => "Error while building Signature object", - - RepositoryFileStatusError => "Error while getting file status", - - GitConfigFetchError => "Error fetching git config", - GitConfigEditorFetchError => "Error fetching 'editor' from git config", - EditorError => "Error while calling editor" - ); -); - -impl GitHookError { - - pub fn inside_of(self, h: HEK) -> HookResult { - Err(HE::new(h, Some(Box::new(self)))) - } - -} - -impl From for HE { - - fn from(he: GitHookError) -> HE { - HE::new(HEK::HookExecutionError, Some(Box::new(he))) - } - -} - -impl From for GitHookError { - - fn from(ge: Git2Error) -> GitHookError { - GitHookError::new(GitHookErrorKind::RepositoryBackendError, Some(Box::new(ge))) - } - -} - -pub trait MapIntoHookError { - fn map_into_hook_error(self) -> Result; -} - -impl MapIntoHookError for Result { - - fn map_into_hook_error(self) -> Result { - self.map_err(|e| HE::new(HEK::HookExecutionError, Some(Box::new(e)))) - } - -} - -pub use self::error::GitHookError; -pub use self::error::GitHookErrorKind; -pub use self::error::MapErrInto; - diff --git a/libimagstorestdhook/src/vcs/git/mod.rs b/libimagstorestdhook/src/vcs/git/mod.rs deleted file mode 100644 index 7fc504b3..00000000 --- a/libimagstorestdhook/src/vcs/git/mod.rs +++ /dev/null @@ -1,29 +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 -// - -mod action; -mod config; -pub mod delete; -mod error; -mod result; -mod runtime; -pub mod store_unload; -pub mod update; -pub mod util; - diff --git a/libimagstorestdhook/src/vcs/git/result.rs b/libimagstorestdhook/src/vcs/git/result.rs deleted file mode 100644 index bf161eb5..00000000 --- a/libimagstorestdhook/src/vcs/git/result.rs +++ /dev/null @@ -1,24 +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::result::Result as RResult; - -use vcs::git::error::GitHookError; - -pub type Result = RResult; diff --git a/libimagstorestdhook/src/vcs/git/runtime.rs b/libimagstorestdhook/src/vcs/git/runtime.rs deleted file mode 100644 index 27fde40c..00000000 --- a/libimagstorestdhook/src/vcs/git/runtime.rs +++ /dev/null @@ -1,178 +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::path::PathBuf; - -use git2::Repository; -use toml::Value; - -use libimagerror::into::IntoError; -use libimagerror::trace::MapErrTrace; -use libimagstore::hook::error::CustomData; -use libimagstore::hook::error::HookErrorKind as HEK; -use libimagstore::hook::result::HookResult; -use libimagutil::debug_result::*; - -use vcs::git::action::StoreAction; -use vcs::git::result::Result; -use vcs::git::error::{MapErrInto, GitHookErrorKind as GHEK}; - -/// Runtime object for git hook implementations. -/// -/// Contains some utility functionality to hold the repository and the configuration for the hooks. -pub struct Runtime { - repository: Option, - config: Option, -} - -impl Runtime { - - /// Build a `Runtime` object, pass the store path to build the `Repository` instance the - /// `Runtime` has to contain. - /// - /// If the building of the `Repository` fails, this function `trace_error()`s the error and - /// returns a `Runtime` object that does _not_ contain a `Repository`. - pub fn new(storepath: &PathBuf) -> Runtime { - Runtime { - repository: Repository::open(storepath).map_err_trace().ok(), - config: None, - } - } - - /// Set the configuration for the `Runtime`. Always returns `Ok(())`. - pub fn set_config(&mut self, cfg: &Value) -> Result<()> { - self.config = Some(cfg.clone()); - Ok(()) - } - - /// Check whether the `Runtime` has a `Repository` - pub fn has_repository(&self) -> bool { - self.repository.is_some() - } - - /// Check whether the `Runtime` has a configuration - pub fn has_config(&self) -> bool { - self.config.is_some() - } - - /// Get the the config value by reference or get an `Err()` which can be returned to the callee - /// of the Hook. - /// - /// The `action` Argument is required in case of `Err()` so the error message can be build - /// correctly. - pub fn config_value_or_err(&self, action: &StoreAction) -> HookResult<&Value> { - self.config - .as_ref() - .ok_or(GHEK::NoConfigError.into_error()) - .map_err_into(GHEK::ConfigError) - .map_err(Box::new) - .map_err(|e| HEK::HookExecutionError.into_error_with_cause(e)) - .map_err(|e| e.with_custom_data(CustomData::default().aborting(false))) - .map_dbg_err(|_| { - format!("[GIT {} HOOK]: Couldn't get Value object from config", action.uppercase()) - }) - } - - /// Get the `Repository` object from the `Runtime` or an `Err()` that can be returned to the - /// callee of the Hook. - /// - /// The `action` Argument is required in case of `Err()` so the error message can be build - /// correctly. - pub fn repository(&self, action: &StoreAction) -> HookResult<&Repository> { - use vcs::git::error::MapIntoHookError; - - debug!("[GIT {} HOOK]: Getting repository", action.uppercase()); - self.repository - .as_ref() - .ok_or(GHEK::MkRepo.into_error()) - .map_err_into(GHEK::RepositoryError) - .map_into_hook_error() - .map_dbg_err(|_| format!("[GIT {} HOOK]: Couldn't fetch Repository", action.uppercase())) - .map_dbg(|_| format!("[GIT {} HOOK]: Repository object fetched", action.uppercase())) - } - - /// Ensure that the branch that is put in the configuration file is checked out, if any. - pub fn ensure_cfg_branch_is_checked_out(&self, action: &StoreAction) -> HookResult<()> { - use vcs::git::config::ensure_branch; - use vcs::git::config::do_checkout_ensure_branch; - - debug!("[GIT {} HOOK]: Ensuring branch checkout", action.uppercase()); - let head = try!(self - .repository(action) - .and_then(|r| { - debug!("[GIT {} HOOK]: Repository fetched, getting head", action.uppercase()); - r.head() - .map_dbg_err_str("Couldn't fetch HEAD") - .map_dbg_err(|e| format!("\tbecause = {:?}", e)) - .map_err_into(GHEK::HeadFetchError) - .map_err(|e| e.into()) - })); - debug!("[GIT {} HOOK]: HEAD fetched", action.uppercase()); - - // TODO: Fail if not on branch? hmmh... I'm not sure - if !head.is_branch() { - debug!("[GIT {} HOOK]: HEAD is not a branch", action.uppercase()); - return Err(GHEK::NotOnBranch.into_error().into()); - } - debug!("[GIT {} HOOK]: HEAD is a branch", action.uppercase()); - - // Check out appropriate branch ... or fail - match ensure_branch(self.config.as_ref()) { - Ok(Some(s)) => { - debug!("[GIT {} HOOK]: We have to ensure branch: {}", action.uppercase(), s); - match head.name().map(|name| { - debug!("[GIT {} HOOK]: {} == {}", action.uppercase(), name, s); - name == s - }) { - Some(b) => { - if b { - debug!("[GIT {} HOOK]: Branch already checked out.", action.uppercase()); - Ok(()) - } else { - debug!("[GIT {} HOOK]: Branch not checked out.", action.uppercase()); - - if !do_checkout_ensure_branch(self.config.as_ref()) { - Err(GHEK::RepositoryWrongBranchError.into_error()) - .map_err_into(GHEK::RepositoryError) - } else { - // Else try to check out the branch... - unimplemented!() - } - } - }, - - None => Err(GHEK::RepositoryBranchNameFetchingError.into_error()) - .map_err_into(GHEK::RepositoryBranchError) - .map_err_into(GHEK::RepositoryError), - } - }, - Ok(None) => { - debug!("[GIT {} HOOK]: No branch to checkout", action.uppercase()); - Ok(()) - }, - - Err(e) => Err(e).map_err_into(GHEK::RepositoryError), - } - .map_err(Box::new) - .map_err(|e| HEK::HookExecutionError.into_error_with_cause(e)) - .map_dbg(|_| format!("[GIT {} HOOK]: Branch checked out", action.uppercase())) - } - -} - diff --git a/libimagstorestdhook/src/vcs/git/store_unload.rs b/libimagstorestdhook/src/vcs/git/store_unload.rs deleted file mode 100644 index 52d8860f..00000000 --- a/libimagstorestdhook/src/vcs/git/store_unload.rs +++ /dev/null @@ -1,264 +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::path::PathBuf; -use std::fmt::{Debug, Formatter, Error as FmtError}; -use std::result::Result as RResult; - -use toml::Value; - -use libimagerror::trace::trace_error; -use libimagstore::storeid::StoreId; -use libimagstore::hook::Hook; -use libimagstore::hook::result::HookResult; -use libimagstore::hook::accessor::{HookDataAccessor, HookDataAccessorProvider}; -use libimagstore::hook::accessor::StoreIdAccessor; -use libimagutil::debug_result::*; - -use vcs::git::error::GitHookErrorKind as GHEK; -use vcs::git::error::MapErrInto; -use vcs::git::runtime::Runtime as GRuntime; - -pub struct StoreUnloadHook { - storepath: PathBuf, - - runtime: GRuntime, -} - -impl StoreUnloadHook { - - pub fn new(storepath: PathBuf) -> StoreUnloadHook { - StoreUnloadHook { - runtime: GRuntime::new(&storepath), - storepath: storepath, - } - } - -} - -impl Debug for StoreUnloadHook { - fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> { - write!(fmt, "StoreUnloadHook(storepath={:?}, repository={}, cfg={:?})", - self.storepath, - (if self.runtime.has_repository() { "Some(_)" } else { "None" }), - self.runtime.has_config()) - } -} - - -impl Hook for StoreUnloadHook { - - fn name(&self) -> &'static str { - "stdhook_git_storeunload" - } - - /// Set the configuration of the hook. See - /// `libimagstorestdhook::vcs::git::runtime::Runtime::set_config()`. - /// - /// This function traces the error (using `trace_error()`) that - /// `libimagstorestdhook::vcs::git::runtime::Runtime::set_config()` - /// returns, if any. - fn set_config(&mut self, config: &Value) { - if let Err(e) = self.runtime.set_config(config) { - trace_error(&e); - } - } - -} - -impl HookDataAccessorProvider for StoreUnloadHook { - - fn accessor(&self) -> HookDataAccessor { - HookDataAccessor::StoreIdAccess(self) - } -} - -impl StoreIdAccessor for StoreUnloadHook { - - fn access(&self, id: &StoreId) -> HookResult<()> { - use libimagerror::into::IntoError; - use vcs::git::action::StoreAction; - use vcs::git::config::commit_message; - use vcs::git::error::MapIntoHookError; - use vcs::git::util::fetch_index; - use vcs::git::config::abort_on_repo_init_err; - use vcs::git::config::is_enabled; - use vcs::git::config::committing_is_enabled; - use vcs::git::config::add_wt_changes_before_committing; - - use git2::{ADD_DEFAULT, - StatusOptions, - Status, - StatusShow as STShow, - STATUS_INDEX_NEW as I_NEW, - STATUS_INDEX_DELETED as I_DEL, - STATUS_INDEX_RENAMED as I_REN, - STATUS_INDEX_MODIFIED as I_MOD, - STATUS_WT_NEW as WT_NEW, - STATUS_WT_DELETED as WT_DEL, - STATUS_WT_RENAMED as WT_REN, - STATUS_WT_MODIFIED as WT_MOD}; - - let action = StoreAction::StoreUnload; - let cfg = try!(self.runtime.config_value_or_err(&action)); - - if !is_enabled(cfg) { - return Ok(()) - } - - if !self.runtime.has_repository() { - debug!("[GIT STORE UNLOAD HOOK]: Runtime has no repository..."); - if try!(self.runtime.config_value_or_err(&action).map(|c| abort_on_repo_init_err(c))) { - // Abort on repo init failure - debug!("[GIT STORE UNLOAD HOOK]: Config says we should abort if we have no repository"); - debug!("[GIT STORE UNLOAD HOOK]: Returing Err(_)"); - return Err(GHEK::RepositoryInitError.into_error()) - .map_err_into(GHEK::RepositoryError) - .map_into_hook_error() - } else { - debug!("[GIT STORE UNLOAD HOOK]: Config says it is okay to not have a repository"); - debug!("[GIT STORE UNLOAD HOOK]: Returing Ok(())"); - return Ok(()) - } - } - - let _ = try!(self.runtime.ensure_cfg_branch_is_checked_out(&action)); - let repo = try!(self.runtime.repository(&action)); - let mut index = try!(fetch_index(repo, &action)); - - let check_dirty = |show: STShow, new: Status, modif: Status, del: Status, ren: Status| { - let mut status_options = StatusOptions::new(); - status_options.show(show); - status_options.include_untracked(true); - - repo.statuses(Some(&mut status_options)) - .map(|statuses| { - statuses.iter() - .map(|s| s.status()) - .map(|s| { - debug!("STATUS_WT_NEW = {}", s == new); - debug!("STATUS_WT_MODIFIED = {}", s == modif); - debug!("STATUS_WT_DELETED = {}", s == del); - debug!("STATUS_WT_RENAMED = {}", s == ren); - s - }) - .any(|s| s == new || s == modif || s == del || s == ren) - }) - .map_err_into(GHEK::RepositoryStatusFetchError) - .map_err_into(GHEK::RepositoryError) - .map_into_hook_error() - }; - - if try!(check_dirty(STShow::Workdir, WT_NEW, WT_MOD, WT_DEL, WT_REN)) { - if add_wt_changes_before_committing(cfg) { - debug!("Adding WT changes before committing."); - try!(index.add_all(&["*"], ADD_DEFAULT, None) - .map_err_into(GHEK::RepositoryPathAddingError) - .map_err_into(GHEK::RepositoryError) - .map_into_hook_error()); - } else { - warn!("WT dirty, but adding files before committing on Drop disabled."); - warn!("Continuing without adding changes to the index."); - } - } else { - debug!("WT not dirty."); - } - - if try!(check_dirty(STShow::Index, I_NEW, I_MOD, I_DEL, I_REN)) { - debug!("INDEX DIRTY!"); - } else { - debug!("INDEX CLEAN... not continuing!"); - return Ok(()); - } - - let signature = try!( - repo.signature() - .map_err_into(GHEK::MkSignature) - .map_dbg_err_str("Failed to fetch signature") - .map_dbg_str("[GIT STORE UNLOAD HOOK]: Fetched signature object") - .map_into_hook_error() - ); - - let head = try!( - repo.head() - .map_err_into(GHEK::HeadFetchError) - .map_dbg_err_str("Failed to fetch HEAD") - .map_dbg_str("[GIT STORE UNLOAD HOOK]: Fetched HEAD") - .map_into_hook_error() - ); - - let tree_id = try!( - index.write_tree() - .map_err_into(GHEK::RepositoryIndexWritingError) - .map_dbg_err_str("Failed to write tree") - .map_dbg_str("[GIT STORE UNLOAD HOOK]: Wrote index tree") - .map_into_hook_error() - ); - - if !try!(committing_is_enabled(cfg)) { - debug!("Committing not enabled. This is fine, returning now..."); - return Ok(()) - } - - let mut parents = Vec::new(); - { - let commit = try!( - repo.find_commit(head.target().unwrap()) - .map_err_into(GHEK::RepositoryParentFetchingError) - .map_dbg_err_str("Failed to find commit HEAD") - .map_dbg_str("[GIT STORE UNLOAD HOOK]: Found commit HEAD") - .map_into_hook_error() - ); - parents.push(commit); - } - - // for converting from Vec to Vec<&Commit> - let parents = parents.iter().collect::>(); - - let tree = try!( - repo.find_tree(tree_id) - .map_err_into(GHEK::RepositoryParentFetchingError) - .map_dbg_err_str("Failed to find tree") - .map_dbg_str("[GIT STORE UNLOAD HOOK]: Found tree for index") - .map_into_hook_error() - ); - - let message = try!(commit_message(&repo, cfg, action, &id) - .map_dbg_err_str("Failed to get commit message") - .map_dbg_str("[GIT STORE UNLOAD HOOK]: Got commit message")); - - try!(repo.commit(Some("HEAD"), &signature, &signature, &message, &tree, &parents) - .map_dbg_str("Committed") - .map_dbg_err_str("Failed to commit") - .map_dbg_str("[GIT STORE UNLOAD HOOK]: Committed") - .map_err_into(GHEK::RepositoryCommittingError) - .map_into_hook_error() - ); - - index.write() - .map_err_into(GHEK::RepositoryIndexWritingError) - .map_dbg_err_str("Failed to write tree") - .map_dbg_str("[GIT STORE UNLOAD HOOK]: Wrote index") - .map_into_hook_error() - .map(|_| ()) - } - -} - - diff --git a/libimagstorestdhook/src/vcs/git/update.rs b/libimagstorestdhook/src/vcs/git/update.rs deleted file mode 100644 index f017c87b..00000000 --- a/libimagstorestdhook/src/vcs/git/update.rs +++ /dev/null @@ -1,263 +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::path::PathBuf; -use std::path::Path; -use std::fmt::{Debug, Formatter, Error as FmtError}; -use std::result::Result as RResult; - -use toml::Value; - -use libimagerror::trace::trace_error; -use libimagstore::hook::Hook; -use libimagstore::hook::accessor::StoreIdAccessor; -use libimagstore::hook::accessor::{HookDataAccessor, HookDataAccessorProvider}; -use libimagstore::hook::position::HookPosition; -use libimagstore::hook::result::HookResult; -use libimagstore::storeid::StoreId; -use libimagutil::debug_result::*; - -use vcs::git::error::GitHookErrorKind as GHEK; -use vcs::git::error::MapErrInto; -use vcs::git::runtime::Runtime as GRuntime; - -/// The `UpdateHook` type -/// -/// Represents a hook which is executed whenever a entry in the store is updated (written to disk). -/// -/// # Time of execution -/// -/// This hook is executed _after_ the store operation succeeded, so _after_ the file is written to -/// disk. -pub struct UpdateHook { - storepath: PathBuf, - - runtime: GRuntime, - - position: HookPosition, -} - -impl UpdateHook { - - pub fn new(storepath: PathBuf, p: HookPosition) -> UpdateHook { - UpdateHook { - runtime: GRuntime::new(&storepath), - storepath: storepath, - position: p, - } - } - -} - -impl Debug for UpdateHook { - fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> { - write!(fmt, "UpdateHook(storepath={:?}, repository={}, pos={:?}, cfg={:?})", - self.storepath, - (if self.runtime.has_repository() { "Some(_)" } else { "None" }), - self.position, - self.runtime.has_config()) - } -} - -impl Hook for UpdateHook { - - fn name(&self) -> &'static str { - "stdhook_git_update" - } - - /// Set the configuration of the hook. See - /// `libimagstorestdhook::vcs::git::runtime::Runtime::set_config()`. - /// - /// This function traces the error (using `trace_error()`) that - /// `libimagstorestdhook::vcs::git::runtime::Runtime::set_config()` - /// returns, if any. - fn set_config(&mut self, config: &Value) { - if let Err(e) = self.runtime.set_config(config) { - trace_error(&e); - } - } - -} - -impl HookDataAccessorProvider for UpdateHook { - - fn accessor(&self) -> HookDataAccessor { - HookDataAccessor::StoreIdAccess(self) - } -} - -impl StoreIdAccessor for UpdateHook { - - /// The implementation of the UpdateHook - /// - /// # Scope - /// - /// This hook takes the git index and commits it either interactively or with a default message, - /// if there is no configuration for an interactive commit. - /// - fn access(&self, id: &StoreId) -> HookResult<()> { - use libimagerror::into::IntoError; - use vcs::git::action::StoreAction; - use vcs::git::config::commit_message; - use vcs::git::error::MapIntoHookError; - use vcs::git::util::fetch_index; - use vcs::git::config::abort_on_repo_init_err; - use vcs::git::config::is_enabled; - use vcs::git::config::committing_is_enabled; - use git2::{ADD_DEFAULT, STATUS_WT_NEW, STATUS_WT_MODIFIED, IndexMatchedPath}; - - debug!("[GIT UPDATE HOOK]: {:?}", id); - - let action = StoreAction::Update; - let cfg = try!(self.runtime.config_value_or_err(&action)); - - if !is_enabled(cfg) { - return Ok(()); - } - - if !self.runtime.has_repository() { - debug!("[GIT UPDATE HOOK]: Runtime has no repository..."); - if try!(self.runtime.config_value_or_err(&action).map(|c| abort_on_repo_init_err(c))) { - // Abort on repo init failure - debug!("[GIT UPDATE HOOK]: Config says we should abort if we have no repository"); - debug!("[GIT UPDATE HOOK]: Returing Err(_)"); - return Err(GHEK::RepositoryInitError.into_error()) - .map_err_into(GHEK::RepositoryError) - .map_into_hook_error() - } else { - debug!("[GIT UPDATE HOOK]: Config says it is okay to not have a repository"); - debug!("[GIT UPDATE HOOK]: Returing Ok(())"); - return Ok(()) - } - } - - let _ = try!(self.runtime.ensure_cfg_branch_is_checked_out(&action)); - let repo = try!(self.runtime.repository(&action)); - - let file_status = try!( - repo - .status_file(id.local()) - .map_dbg_err_str("Failed to fetch file status") - .map_dbg_err(|e| format!("\t-> {:?}", e)) - .map_dbg_str("[GIT UPDATE HOOK]: Fetched file status") - .map_err_into(GHEK::RepositoryFileStatusError) - .map_into_hook_error() - ); - - debug!("File status: STATUS_WT_NEW = {}", file_status.contains(STATUS_WT_NEW)); - debug!("File status: STATUS_WT_MODIFIED = {}", file_status.contains(STATUS_WT_MODIFIED)); - - if !file_status.contains(STATUS_WT_NEW) && !file_status.contains(STATUS_WT_MODIFIED) { - // File seems to be unmodified and not new. This means that the file is already - // committed and we can return here. - return Ok(()) - } - - let mut index = try!(fetch_index(repo, &action)); - - let signature = try!( - repo.signature() - .map_err_into(GHEK::MkSignature) - .map_dbg_err_str("Failed to fetch signature") - .map_dbg_str("[GIT UPDATE HOOK]: Fetched signature object") - .map_into_hook_error() - ); - - let head = try!( - repo.head() - .map_err_into(GHEK::HeadFetchError) - .map_dbg_err_str("Failed to fetch HEAD") - .map_dbg_str("[GIT UPDATE HOOK]: Fetched HEAD") - .map_into_hook_error() - ); - - let cb = &mut |path: &Path, _matched_spec: &[u8]| -> i32 { - if file_status.contains(STATUS_WT_NEW) || file_status.contains(STATUS_WT_MODIFIED) { - debug!("[GIT CREATE HOOK]: File is modified/new: {}", path.display()); - 0 - } else { - debug!("[GIT CREATE HOOK]: Ignoring file: {}", path.display()); - 1 - } - }; - - try!( - index.add_all(&[id.local()], ADD_DEFAULT, Some(cb as &mut IndexMatchedPath)) - .map_err_into(GHEK::RepositoryPathAddingError) - .map_dbg_err_str("Failed to add to index") - .map_dbg(|_| format!("[GIT UPDATE HOOK]: Added id ({:?}) to index", id)) - .map_into_hook_error() - ); - - let tree_id = try!( - index.write_tree() - .map_err_into(GHEK::RepositoryIndexWritingError) - .map_dbg_err_str("Failed to write tree") - .map_dbg_str("[GIT UPDATE HOOK]: Wrote tree") - .map_into_hook_error() - ); - - if !try!(committing_is_enabled(cfg)) { - debug!("Committing not enabled. This is fine, returning now..."); - return Ok(()) - } - - let mut parents = Vec::new(); - { - let commit = try!( - repo.find_commit(head.target().unwrap()) - .map_err_into(GHEK::RepositoryParentFetchingError) - .map_dbg_err_str("Failed to find commit HEAD") - .map_dbg_str("[GIT UPDATE HOOK]: Fetched commit parents of HEAD") - .map_into_hook_error() - ); - parents.push(commit); - } - - // for converting from Vec to Vec<&Commit> - let parents = parents.iter().collect::>(); - - let tree = try!( - repo.find_tree(tree_id) - .map_err_into(GHEK::RepositoryParentFetchingError) - .map_dbg_err_str("Failed to find tree") - .map_dbg_str("[GIT UPDATE HOOK]: Found Tree for parents") - .map_into_hook_error() - ); - - let message = try!(commit_message(&repo, cfg, StoreAction::Update, &id) - .map_dbg_err_str("Failed to get commit message") - .map_dbg_str("[GIT UPDATE HOOK]: Fetched commit message")); - - try!(repo.commit(Some("HEAD"), &signature, &signature, &message, &tree, &parents) - .map_dbg_str("Committed") - .map_dbg_err_str("Failed to commit") - .map_err_into(GHEK::RepositoryCommittingError) - .map_into_hook_error() - ); - - index.write() - .map_err_into(GHEK::RepositoryIndexWritingError) - .map_dbg_err_str("Failed to write tree") - .map_into_hook_error() - .map(|_| ()) - } - -} - diff --git a/libimagstorestdhook/src/vcs/git/util.rs b/libimagstorestdhook/src/vcs/git/util.rs deleted file mode 100644 index bfeb2c2f..00000000 --- a/libimagstorestdhook/src/vcs/git/util.rs +++ /dev/null @@ -1,42 +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 -// - -//! Utility functionality for integrating git hooks in the store -//! -//! Contains primitives to create a repository within the store path - -use git2::{Repository, Index}; - -use vcs::git::error::GitHookErrorKind as GHEK; -use vcs::git::error::MapErrInto; -use vcs::git::action::StoreAction; -use vcs::git::error::MapIntoHookError; - -use libimagutil::debug_result::*; -use libimagstore::hook::error::HookError; - -pub fn fetch_index(repo: &Repository, action: &StoreAction) -> Result { - debug!("[GIT {} HOOK]: Getting Index", action.uppercase()); - repo.index() - .map_dbg_err(|_| format!("[GIT {} HOOK]: Couldn't fetch Index", action.uppercase())) - .map_dbg(|_| format!("[GIT {} HOOK]: Index object fetched", action.uppercase())) - .map_err_into(GHEK::RepositoryIndexFetchingError) - .map_into_hook_error() -} - diff --git a/libimagstorestdhook/src/vcs/mod.rs b/libimagstorestdhook/src/vcs/mod.rs deleted file mode 100644 index 7449c7bc..00000000 --- a/libimagstorestdhook/src/vcs/mod.rs +++ /dev/null @@ -1,20 +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 -// - -pub mod git; From 204ef2470381967c2cd9044c623f83f2eaff7588 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 4 Jun 2017 16:30:43 +0200 Subject: [PATCH 02/23] Remove hook support from store --- libimagstore/src/configuration.rs | 596 +-------------------- libimagstore/src/error.rs | 15 - libimagstore/src/hook/accessor.rs | 49 -- libimagstore/src/hook/aspect.rs | 151 ------ libimagstore/src/hook/error.rs | 65 --- libimagstore/src/hook/mod.rs | 36 -- libimagstore/src/hook/position.rs | 32 -- libimagstore/src/hook/result.rs | 22 - libimagstore/src/lib.rs | 1 - libimagstore/src/store.rs | 860 +----------------------------- 10 files changed, 7 insertions(+), 1820 deletions(-) delete mode 100644 libimagstore/src/hook/accessor.rs delete mode 100644 libimagstore/src/hook/aspect.rs delete mode 100644 libimagstore/src/hook/error.rs delete mode 100644 libimagstore/src/hook/mod.rs delete mode 100644 libimagstore/src/hook/position.rs delete mode 100644 libimagstore/src/hook/result.rs 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()); - } -} From 91dce6858f7bcbddc64b0349f5bb1f5aff56eb20 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 4 Jun 2017 16:31:28 +0200 Subject: [PATCH 03/23] Remove hook config from example config file --- imagrc.toml | 145 ---------------------------------------------------- 1 file changed, 145 deletions(-) diff --git a/imagrc.toml b/imagrc.toml index 37a940df..5c5598b5 100644 --- a/imagrc.toml +++ b/imagrc.toml @@ -33,148 +33,3 @@ readline_prompt = ">> " # lives implicitely implicit-create = false -# Hooks which get executed right before the Store is closed. -# They get the store path as StoreId passed, so they can alter the complete -# store, so these hooks should be chosen carefully. -store-unload-hook-aspects = [ "debug", "vcs" ] - -pre-create-hook-aspects = [ "debug", "vcs" ] -post-create-hook-aspects = [ "debug", "vcs" ] - -pre-move-hook-aspects = [ "debug" ] -post-move-hook-aspects = [ "debug" ] - -pre-retrieve-hook-aspects = [ "debug", "vcs" ] -post-retrieve-hook-aspects = [ "debug", "vcs" ] - -pre-update-hook-aspects = [ "debug", "vcs" ] -post-update-hook-aspects = [ "debug", "vcs" ] - -pre-delete-hook-aspects = [ "debug", "vcs" ] -post-delete-hook-aspects = [ "debug", "vcs" ] - -[store.aspects.debug] -parallel = false -mutable_hooks = true - -[store.aspects.vcs] -parallel = false -mutable_hooks = false - -[store.hooks.stdhook_debug] -aspect = "debug" - -[store.hooks.stdhook_git_update] -aspect = "vcs" - -# set to false to disable -enabled = true - -# Fail if the repository cannot be opened. If this is set to `false`, the error -# will be printed, but will not abort the store operation. `true` will print the -# error and abort the store action. -abort_on_repo_init_failure = true - -# Ensure to be on this branche before doing anything. -ensure_branch = "refs/heads/master" - -# Try to checkout the ensure_branch if it isn't checked out -try_checkout_ensure_branch = true - -# Commit configuration -[store.hooks.stdhook_git_update.commit] - -# Enable committing here. If not enabled, the "stdhook_git_storeunload" hook -# will commit all changes in one commit when the store is closed. -enabled = false - -# Whether to do the commit interactively -interactive = false - -# Set to true to use the $EDITOR for the commit, to false to do on commandline -# When committing without editor, only a single line is allowed as commit -# message -interactive_editor = false - -# Commit message if the commit is not interactive -message = "Update" - -[store.hooks.stdhook_git_delete] -aspect = "vcs" - -# set to false to disable -enabled = true - -# Fail if the repository cannot be opened. If this is set to `false`, the error -# will be printed, but will not abort the store operation. `true` will print the -# error and abort the store action. -abort_on_repo_init_failure = true - -# Ensure to be on this branche before doing anything. -ensure_branch = "refs/heads/master" - -# Try to checkout the ensure_branch if it isn't checked out -try_checkout_ensure_branch = true - -# Commit configuration -[store.hooks.stdhook_git_delete.commit] - -# Enable committing here. If not enabled, the "stdhook_git_storeunload" hook -# will commit all changes in one commit when the store is closed. -enabled = false - -# Whether to do the commit interactively -interactive = false - -# Set to true to use the $EDITOR for the commit, to false to do on commandline -# When committing without editor, only a single line is allowed as commit -# message -interactive_editor = false - -# Commit message if the commit is not interactive -message = "Deleted" - -[store.hooks.stdhook_git_storeunload] -aspect = "vcs" - -# set to false to disable -enabled = true - -# Fail if the repository cannot be opened. If this is set to `false`, the error -# will be printed, but will not abort the store operation. `true` will print the -# error and abort the store action. -abort_on_repo_init_failure = true - -# Ensure to be on this branche before doing anything. -ensure_branch = "refs/heads/master" - -# Try to checkout the ensure_branch if it isn't checked out -try_checkout_ensure_branch = true - -# Commit configuration -[store.hooks.stdhook_git_storeunload.commit] - -# Enable on-unload-committing, causing the store-unload hook to commit the -# changes to the store. This has no effect if the changes were already committed -# by the other git hooks. -enabled = true - -# Do a git-add on all files that are not in the index yet, before committing. -# This must be turned on, as we do not support adding with "Update" hooks and -# only committing with the "Drop" hook, yet. -# So, effectively, disabling this will disable committing. -# -# If not set: false -add_wt_changes = true - -# Whether to do the commit interactively -interactive = false - -# Set to true to use the $EDITOR for the commit, to false to do on commandline -# When committing without editor, only a single line is allowed as commit -# message -interactive_editor = false - -# Commit message if the commit is not interactive -message = "Commit on drop" - From 92548517245568a1f9d078660ea20f8689eeecef Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 4 Jun 2017 16:32:44 +0200 Subject: [PATCH 04/23] doc: Remove hook stuff --- doc/src/05100-lib-rt.md | 1 - doc/src/05100-lib-store-std-hook.md | 17 ----------------- doc/src/05100-lib-store.md | 4 +--- 3 files changed, 1 insertion(+), 21 deletions(-) delete mode 100644 doc/src/05100-lib-store-std-hook.md diff --git a/doc/src/05100-lib-rt.md b/doc/src/05100-lib-rt.md index b296ba7b..ca22256c 100644 --- a/doc/src/05100-lib-rt.md +++ b/doc/src/05100-lib-rt.md @@ -12,5 +12,4 @@ to live in a imag binary. ### Long-term TODO - [ ] Merge with `libimagstore` -- [ ] Merge with `libimagstorestdhook` diff --git a/doc/src/05100-lib-store-std-hook.md b/doc/src/05100-lib-store-std-hook.md deleted file mode 100644 index 72972b0a..00000000 --- a/doc/src/05100-lib-store-std-hook.md +++ /dev/null @@ -1,17 +0,0 @@ -## libimagstorestdhook - -The `libimagstorestdhook` is a library for default store hooks which are shipped -with the imag store. -Hooks are actions which can be performed before and after certain store actions, -for example before a file is created, or after a file is removed. - -### Long-term TODO - -- [ ] Merge with `libimagrt` -- [ ] Merge with `libimagstorestdhook` -- [ ] Create Runtime-wide "Store meta data" storage in the Runtime, which can be - set by users during the runtime of imag and then used by the hooks to get meta - information about their own runtime. -- [ ] Implement parallel store hook execution -- [ ] Implement Non-Mutable store hook execution - diff --git a/doc/src/05100-lib-store.md b/doc/src/05100-lib-store.md index 2055038b..d4119a3e 100644 --- a/doc/src/05100-lib-store.md +++ b/doc/src/05100-lib-store.md @@ -3,8 +3,7 @@ The store is the heart of everything. Here lives the data, the complexity and the performance bottleneck. -The store offeres read/write access to all entries, a hook system to do -on-the-fly modification of incoming/outgoing files and so on. +The store offeres read/write access to all entries. The store itself does not offer functionality, but has a commandline interface "imag-store" which can do basic things with the store. @@ -13,5 +12,4 @@ The store itself does not offer functionality, but has a commandline interface ### Long-term TODO - [ ] Merge with `libimagrt` -- [ ] Merge with `libimagstorestdhook` From 25ff89f5fc8758355a228ece62bd83b98e3f2fe6 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Mon, 5 Jun 2017 21:39:16 +0200 Subject: [PATCH 05/23] Remove hook support from libimagrt --- libimagrt/Cargo.toml | 3 --- libimagrt/src/lib.rs | 1 - libimagrt/src/runtime.rs | 55 +--------------------------------------- 3 files changed, 1 insertion(+), 58 deletions(-) diff --git a/libimagrt/Cargo.toml b/libimagrt/Cargo.toml index 29cf9953..a812e697 100644 --- a/libimagrt/Cargo.toml +++ b/libimagrt/Cargo.toml @@ -26,9 +26,6 @@ ansi_term = "0.9" [dependencies.libimagstore] path = "../libimagstore" -[dependencies.libimagstorestdhook] -path = "../libimagstorestdhook" - [dependencies.libimagutil] path = "../libimagutil" diff --git a/libimagrt/src/lib.rs b/libimagrt/src/lib.rs index 93dc23e4..1670418f 100644 --- a/libimagrt/src/lib.rs +++ b/libimagrt/src/lib.rs @@ -44,7 +44,6 @@ extern crate clap; extern crate toml; extern crate libimagstore; -extern crate libimagstorestdhook; extern crate libimagutil; #[macro_use] extern crate libimagerror; diff --git a/libimagrt/src/runtime.rs b/libimagrt/src/runtime.rs index 07cd86c8..33bcfd53 100644 --- a/libimagrt/src/runtime.rs +++ b/libimagrt/src/runtime.rs @@ -61,15 +61,7 @@ impl<'a> Runtime<'a> { use clap::Shell; - use libimagstore::hook::position::HookPosition as HP; - use libimagstore::hook::Hook; - use libimagstore::error::StoreErrorKind; - use libimagstorestdhook::debug::DebugHook; - use libimagstorestdhook::vcs::git::delete::DeleteHook as GitDeleteHook; - use libimagstorestdhook::vcs::git::update::UpdateHook as GitUpdateHook; - use libimagstorestdhook::vcs::git::store_unload::StoreUnloadHook as GitStoreUnloadHook; use libimagerror::trace::trace_error; - use libimagerror::trace::trace_error_dbg; use libimagerror::into::IntoError; use configuration::error::ConfigErrorKind; @@ -142,52 +134,7 @@ impl<'a> Runtime<'a> { write!(stderr(), "Store-config: {:?}\n", store_config).ok(); } - Store::new(storepath.clone(), store_config).map(|mut store| { - // If we are debugging, generate hooks for all positions - if is_debugging { - let hooks : Vec<(Box, &str, HP)> = vec![ - (Box::new(DebugHook::new(HP::PreCreate)) , "debug", HP::PreCreate), - (Box::new(DebugHook::new(HP::PostCreate)) , "debug", HP::PostCreate), - (Box::new(DebugHook::new(HP::PreRetrieve)) , "debug", HP::PreRetrieve), - (Box::new(DebugHook::new(HP::PostRetrieve)) , "debug", HP::PostRetrieve), - (Box::new(DebugHook::new(HP::PreUpdate)) , "debug", HP::PreUpdate), - (Box::new(DebugHook::new(HP::PostUpdate)) , "debug", HP::PostUpdate), - (Box::new(DebugHook::new(HP::PreDelete)) , "debug", HP::PreDelete), - (Box::new(DebugHook::new(HP::PostDelete)) , "debug", HP::PostDelete), - ]; - - // If hook registration fails, trace the error and warn, but continue. - for (hook, aspectname, position) in hooks { - if let Err(e) = store.register_hook(position, &String::from(aspectname), hook) { - if e.err_type() == StoreErrorKind::HookRegisterError { - trace_error_dbg(&e); - warn!("Registering debug hook with store failed"); - } else { - trace_error(&e); - }; - } - } - } - - let sp = storepath; - - let hooks : Vec<(Box, &str, HP)> = vec![ - (Box::new(GitDeleteHook::new(sp.clone(), HP::PostDelete)), "vcs", HP::PostDelete), - (Box::new(GitUpdateHook::new(sp.clone(), HP::PostUpdate)), "vcs", HP::PostUpdate), - (Box::new(GitStoreUnloadHook::new(sp)), "vcs", HP::StoreUnload), - ]; - - for (hook, aspectname, position) in hooks { - if let Err(e) = store.register_hook(position, &String::from(aspectname), hook) { - if e.err_type() == StoreErrorKind::HookRegisterError { - trace_error_dbg(&e); - warn!("Registering git hook with store failed"); - } else { - trace_error(&e); - }; - } - } - + Store::new(storepath.clone(), store_config).map(|store| { Runtime { cli_matches: matches, configuration: cfg, From e88c5011abb4b0f27512b37ae059ae7f0e4e61da Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Mon, 5 Jun 2017 21:39:29 +0200 Subject: [PATCH 06/23] Remove libimagstorestdhook entry --- CONTRIBUTING.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3f4615d9..a471d497 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -184,7 +184,6 @@ from the others by `"/`". See below: | part/lib/imagnotes | Targets library: imagnotes | [search][search-part/lib/imagnotes] | | part/lib/imagrt | Targets library: imagrt | [search][search-part/lib/imagrt] | | part/lib/imagstore | Targets library: imagstore | [search][search-part/lib/imagstore] | -| part/lib/imagstorestdhook | Targets library: imagstorestdhook | [search][search-part/lib/imagstorestdhook] | | part/lib/imagutil | Targets library: | [search][search-part/lib/imagutil] | | part/_new_binary | Introduces new binary | [search][search-part/_new_binary] | | part/_new_library | Introduces new library | [search][search-part/_new_library] | @@ -283,7 +282,6 @@ _to be written_ [search-part/lib/imagnotes]: https://github.com/matthiasbeyer/imag/labels/part%2Flib%2Fimagnotes [search-part/lib/imagrt]: https://github.com/matthiasbeyer/imag/labels/part%2Flib%2Fimagrt [search-part/lib/imagstore]: https://github.com/matthiasbeyer/imag/labels/part%2Flib%2Fimagstore -[search-part/lib/imagstorestdhook]: https://github.com/matthiasbeyer/imag/labels/part%2Flib%2Fimagstorestdhook [search-part/lib/imagutil]: https://github.com/matthiasbeyer/imag/labels/part%2Flib%2Fimagutil [search-part/_new_binary]: https://github.com/matthiasbeyer/imag/labels/part%2F_new_binary [search-part/_new_library]: https://github.com/matthiasbeyer/imag/labels/part%2F_new_library From a70d7d347a9a11c41b1f90a550ff42e0e66c23f5 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 4 Jun 2017 19:30:13 +0200 Subject: [PATCH 07/23] Add file-system file locking functionality in the store --- libimagstore/Cargo.toml | 5 +++++ libimagstore/src/store.rs | 22 +++++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/libimagstore/Cargo.toml b/libimagstore/Cargo.toml index 4089feda..acee580b 100644 --- a/libimagstore/Cargo.toml +++ b/libimagstore/Cargo.toml @@ -66,3 +66,8 @@ verify = [] # early-panic=[] +# File system locking +# +# Enable this feature to enable file-system locking in the store. +fs-locking = [] + diff --git a/libimagstore/src/store.rs b/libimagstore/src/store.rs index 4d7e811d..333622dc 100644 --- a/libimagstore/src/store.rs +++ b/libimagstore/src/store.rs @@ -130,11 +130,18 @@ impl Iterator for Walk { } } - impl StoreEntry { fn new(id: StoreId) -> Result { let pb = try!(id.clone().into_pathbuf()); + + #[cfg(feature = "fs-lock")] + { + try!(open_file(pb.clone()) + .and_then(|f| f.lock_exclusive().map_err_into(SEK::FileError)) + .map_err_into(SEK::IoError)); + } + Ok(StoreEntry { id: id, file: FileAbstraction::Absent(pb), @@ -176,6 +183,19 @@ impl StoreEntry { } } +#[cfg(feature = "fs-lock")] +impl Drop for StoreEntry { + + fn drop(self) { + self.get_entry() + .and_then(|entry| open_file(entry.get_location().clone()).map_err_into(SEK::IoError)) + .and_then(|f| f.unlock().map_err_into(SEK::FileError)) + .map_err_into(SEK::IoError) + } + +} + + /// The Store itself, through this object one can interact with IMAG's entries pub struct Store { location: PathBuf, From c4584bf1ca1fa4b8bf296995eade307e3df6eb26 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 4 Jun 2017 16:59:23 +0200 Subject: [PATCH 08/23] Add debug output in Store::create() --- libimagstore/src/store.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libimagstore/src/store.rs b/libimagstore/src/store.rs index 4d7e811d..be0ad79e 100644 --- a/libimagstore/src/store.rs +++ b/libimagstore/src/store.rs @@ -333,6 +333,8 @@ impl Store { pub fn create<'a, S: IntoStoreId>(&'a self, id: S) -> Result> { let id = try!(id.into_storeid()).with_base(self.path().clone()); + debug!("Creating id: '{}'", id); + { let mut hsmap = match self.entries.write() { Err(_) => return Err(SEK::LockPoisoned.into_error()).map_err_into(SEK::CreateCallError), @@ -340,15 +342,19 @@ impl Store { }; if hsmap.contains_key(&id) { + debug!("Cannot create, internal cache already contains: '{}'", id); return Err(SEK::EntryAlreadyExists.into_error()).map_err_into(SEK::CreateCallError); } hsmap.insert(id.clone(), { + debug!("Creating: '{}'", id); let mut se = try!(StoreEntry::new(id.clone())); se.status = StoreEntryStatus::Borrowed; se }); } + debug!("Constructing FileLockEntry: '{}'", id); + Ok(FileLockEntry::new(self, Entry::new(id))) } From 6cb5300cd995da49d0f8abd3bc31323e61884441 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 4 Jun 2017 19:04:42 +0200 Subject: [PATCH 09/23] Add debug output to Store::retrieve --- libimagstore/src/store.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libimagstore/src/store.rs b/libimagstore/src/store.rs index be0ad79e..5a2b2262 100644 --- a/libimagstore/src/store.rs +++ b/libimagstore/src/store.rs @@ -374,6 +374,7 @@ impl Store { /// pub fn retrieve<'a, S: IntoStoreId>(&'a self, id: S) -> Result> { let id = try!(id.into_storeid()).with_base(self.path().clone()); + debug!("Retrieving id: '{}'", id); let entry = try!({ self.entries .write() @@ -388,6 +389,7 @@ impl Store { .map_err_into(SEK::RetrieveCallError) }); + debug!("Constructing FileLockEntry: '{}'", id); Ok(FileLockEntry::new(self, entry)) } From 13e80445ae47c69004c7b900cf62df1ae184a084 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 4 Jun 2017 19:05:35 +0200 Subject: [PATCH 10/23] Add debug output to Store::get --- libimagstore/src/store.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libimagstore/src/store.rs b/libimagstore/src/store.rs index 5a2b2262..a842cd0b 100644 --- a/libimagstore/src/store.rs +++ b/libimagstore/src/store.rs @@ -406,6 +406,8 @@ impl Store { pub fn get<'a, S: IntoStoreId + Clone>(&'a self, id: S) -> Result>> { let id = try!(id.into_storeid()).with_base(self.path().clone()); + debug!("Getting id: '{}'", id); + let exists = try!(id.exists()) || try!(self.entries .read() .map(|map| map.contains_key(&id)) From a3466ae5480dc98b1a6e833fb3613178c2dc3a81 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 4 Jun 2017 19:06:22 +0200 Subject: [PATCH 11/23] Add debug output to Store::retrieve_for_module --- libimagstore/src/store.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libimagstore/src/store.rs b/libimagstore/src/store.rs index a842cd0b..10f56cf2 100644 --- a/libimagstore/src/store.rs +++ b/libimagstore/src/store.rs @@ -438,6 +438,8 @@ impl Store { let mut path = self.path().clone(); path.push(mod_name); + debug!("Retrieving for module: '{}'", mod_name); + path.to_str() .ok_or(SE::new(SEK::EncodingError, None)) .and_then(|path| { From bc80a3b7d5bc208dc9996a287d10db39301b5d45 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 4 Jun 2017 19:06:54 +0200 Subject: [PATCH 12/23] Add debug output to Store::walk --- libimagstore/src/store.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/libimagstore/src/store.rs b/libimagstore/src/store.rs index 10f56cf2..e86ba894 100644 --- a/libimagstore/src/store.rs +++ b/libimagstore/src/store.rs @@ -457,6 +457,7 @@ impl Store { /// The difference between a `Walk` and a `StoreIdIterator` is that with a `Walk`, one can find /// "collections" (folders). pub fn walk<'a>(&'a self, mod_name: &str) -> Walk { + debug!("Creating Walk object for {}", mod_name); Walk::new(self.path().clone(), mod_name) } From ef0a76a02f9e024d9224dfa0e6529b84c9416406 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 4 Jun 2017 19:08:47 +0200 Subject: [PATCH 13/23] Add debug output to Store::update --- libimagstore/src/store.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/libimagstore/src/store.rs b/libimagstore/src/store.rs index e86ba894..e11aa29f 100644 --- a/libimagstore/src/store.rs +++ b/libimagstore/src/store.rs @@ -466,6 +466,7 @@ impl Store { /// See `Store::_update()`. /// pub fn update<'a>(&'a self, entry: &mut FileLockEntry<'a>) -> Result<()> { + debug!("Updating FileLockEntry at '{}'", entry.get_location()); self._update(entry, false).map_err_into(SEK::UpdateCallError) } From 79623e1db27e27500566c41cf1b8461543d45502 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 4 Jun 2017 19:11:18 +0200 Subject: [PATCH 14/23] Add debug output to Store::retrieve_copy --- libimagstore/src/store.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/libimagstore/src/store.rs b/libimagstore/src/store.rs index e11aa29f..588a8b78 100644 --- a/libimagstore/src/store.rs +++ b/libimagstore/src/store.rs @@ -523,6 +523,7 @@ impl Store { /// pub fn retrieve_copy(&self, id: S) -> Result { let id = try!(id.into_storeid()).with_base(self.path().clone()); + debug!("Retrieving copy of '{}'", id); let entries = match self.entries.write() { Err(_) => { return Err(SE::new(SEK::LockPoisoned, None)) From fc854f3647ea8b22b0d35804c8b24241c9333490 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 4 Jun 2017 19:12:03 +0200 Subject: [PATCH 15/23] Add debug output to Store::delete --- libimagstore/src/store.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libimagstore/src/store.rs b/libimagstore/src/store.rs index 588a8b78..2f2867a3 100644 --- a/libimagstore/src/store.rs +++ b/libimagstore/src/store.rs @@ -554,6 +554,8 @@ impl Store { pub fn delete(&self, id: S) -> Result<()> { let id = try!(id.into_storeid()).with_base(self.path().clone()); + debug!("Deleting id: '{}'", id); + { let mut entries = match self.entries.write() { Err(_) => return Err(SE::new(SEK::LockPoisoned, None)) @@ -580,6 +582,7 @@ impl Store { } } + debug!("Deleted"); Ok(()) } From e157a906bd31486db4b0eae24cc27f3dd485819b Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 4 Jun 2017 19:13:06 +0200 Subject: [PATCH 16/23] Add debug output to Store::save_to --- libimagstore/src/store.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/libimagstore/src/store.rs b/libimagstore/src/store.rs index 2f2867a3..604cfcc3 100644 --- a/libimagstore/src/store.rs +++ b/libimagstore/src/store.rs @@ -588,6 +588,7 @@ impl Store { /// Save a copy of the Entry in another place pub fn save_to(&self, entry: &FileLockEntry, new_id: StoreId) -> Result<()> { + debug!("Saving '{}' to '{}'", entry.get_location(), new_id); self.save_to_other_location(entry, new_id, false) } From c06d8c319f53e441e45c9347114715bd88bf134c Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 4 Jun 2017 19:13:24 +0200 Subject: [PATCH 17/23] Add debug output to Store::save_as --- libimagstore/src/store.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/libimagstore/src/store.rs b/libimagstore/src/store.rs index 604cfcc3..4fa556d8 100644 --- a/libimagstore/src/store.rs +++ b/libimagstore/src/store.rs @@ -595,6 +595,7 @@ impl Store { /// Save an Entry in another place /// Removes the original entry pub fn save_as(&self, entry: FileLockEntry, new_id: StoreId) -> Result<()> { + debug!("Saving '{}' as '{}'", entry.get_location(), new_id); self.save_to_other_location(&entry, new_id, true) } From 1c18db56e6d8aa44c43ed39c814c3999e4d7a5c1 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 4 Jun 2017 19:14:41 +0200 Subject: [PATCH 18/23] Add debug output to Store::save_to_other_location --- libimagstore/src/store.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/libimagstore/src/store.rs b/libimagstore/src/store.rs index 4fa556d8..8696d107 100644 --- a/libimagstore/src/store.rs +++ b/libimagstore/src/store.rs @@ -621,6 +621,7 @@ impl Store { FileAbstraction::copy(&old_id_as_path, &new_id_as_path) .and_then(|_| { if remove_old { + debug!("Removing old '{:?}'", old_id_as_path); FileAbstraction::remove_file(&old_id_as_path) } else { Ok(()) From fa0e8130b6df5fef6cb4035ad8a99f182703b7bf Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 4 Jun 2017 19:15:42 +0200 Subject: [PATCH 19/23] Add debug output to Store::move_by_id --- libimagstore/src/store.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libimagstore/src/store.rs b/libimagstore/src/store.rs index 8696d107..7d12a270 100644 --- a/libimagstore/src/store.rs +++ b/libimagstore/src/store.rs @@ -668,6 +668,8 @@ impl Store { let new_id = new_id.with_base(self.path().clone()); let old_id = old_id.with_base(self.path().clone()); + debug!("Moving '{}' to '{}'", old_id, new_id); + { let mut hsmap = match self.entries.write() { Err(_) => return Err(SE::new(SEK::LockPoisoned, None)), @@ -706,6 +708,7 @@ impl Store { } + debug!("Moved"); Ok(()) } From 33390acf234af23cb08b1278fbac7e569b4c602d Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 4 Jun 2017 16:48:02 +0200 Subject: [PATCH 20/23] Allow custom data in errors to be displayed --- libimagerror/src/error_gen.rs | 18 +++++++++++++++++- libimagstore/src/error.rs | 6 ++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/libimagerror/src/error_gen.rs b/libimagerror/src/error_gen.rs index 7a9fc18a..ccfd1575 100644 --- a/libimagerror/src/error_gen.rs +++ b/libimagerror/src/error_gen.rs @@ -118,7 +118,10 @@ macro_rules! generate_custom_error_types { fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> { try!(write!(fmt, "[{}]", self.err_type)); - Ok(()) + match self.custom_data { + Some(ref c) => write!(fmt, "{}", c), + None => Ok(()), + } } } @@ -215,6 +218,13 @@ macro_rules! generate_error_types { ) => { #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Copy)] pub struct SomeNotExistingTypeWithATypeNameNoOneWillEverChoose {} + + impl Display for SomeNotExistingTypeWithATypeNameNoOneWillEverChoose { + fn fmt(&self, _: &mut Formatter) -> Result<(), FmtError> { + Ok(()) + } + } + generate_custom_error_types!($name, $kindname, SomeNotExistingTypeWithATypeNameNoOneWillEverChoose, $($kind => $string),*); @@ -241,6 +251,12 @@ mod test { pub othr: i64, } + impl Display for CustomData { + fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> { + Ok(()) + } + } + generate_error_imports!(); #[allow(dead_code)] diff --git a/libimagstore/src/error.rs b/libimagstore/src/error.rs index 3fe5a360..178a5e8f 100644 --- a/libimagstore/src/error.rs +++ b/libimagstore/src/error.rs @@ -23,6 +23,12 @@ use std::convert::From; #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Copy)] pub struct CustomErrorData {} +impl Display for CustomErrorData { + fn fmt(&self, _: &mut Formatter) -> Result<(), FmtError> { + Ok(()) // Do nothing here, we don't need to print smth + } +} + generate_custom_error_types!(StoreError, StoreErrorKind, CustomErrorData, ConfigurationError => "Store Configuration Error", ConfigTypeError => "Store configuration type error", From 83647e52a0c994bbd5355460dd390c4c16e405d8 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 4 Jun 2017 16:52:32 +0200 Subject: [PATCH 21/23] Add trait to extend store for link consistency check This commit adds the error infrastructure as well as the trait for extensing the `Store` type with a function to do a link consistency check over _all_ entries in the store. The functionality is not implemented yet. --- libimagentrylink/src/internal.rs | 51 ++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/libimagentrylink/src/internal.rs b/libimagentrylink/src/internal.rs index 5949495e..9c2cc63e 100644 --- a/libimagentrylink/src/internal.rs +++ b/libimagentrylink/src/internal.rs @@ -585,6 +585,57 @@ fn process_rw_result(links: StoreResult>) -> Result { Ok(LinkIter::new(links)) } +pub mod store_check { + + pub mod error { + generate_error_imports!(); + + use libimagstore::storeid::StoreId; + + #[derive(Debug)] + pub enum StoreLinkConsistencyErrorCustomData { + DeadLink { + target: StoreId + }, + OneDirectionalLink { + source: StoreId, + target: StoreId + }, + } + + impl Display for StoreLinkConsistencyErrorCustomData { + } + + generate_custom_error_types!( + StoreLinkConsistencyError, + StoreLinkConsistencyErrorKind, + StoreLinkConsistencyErrorCustomData, + StoreLinkConsistencyError => "Links in the store are not consistent" + ); + + generate_result_helper!(StoreLinkConsistencyError, StoreLinkConsistencyErrorKind); + generate_option_helper!(StoreLinkConsistencyError, StoreLinkConsistencyErrorKind); + } + + pub use self::error::StoreLinkConsistencyError; + pub use self::error::StoreLinkConsistencyErrorKind; + pub use self::error::MapErrInto; + + pub mod result { + use std::result::Result as RResult; + use internal::store_check::error::StoreLinkConsistencyError as SLCE; + + pub type Result = RResult; + } + + pub trait StoreLinkConsistentExt { + fn check_link_consistency(&self) -> Result<()> { + unimplemented!() + } + } + +} + #[cfg(test)] mod test { use std::path::PathBuf; From 13b24cb397c68f0179a15e961f5e6b14b4eeecd9 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Tue, 6 Jun 2017 15:15:08 +0200 Subject: [PATCH 22/23] Impl StoreLinkConsistentExt::check_link_consistency() --- libimagentrylink/src/internal.rs | 193 ++++++++++++++++++++++++++++++- 1 file changed, 191 insertions(+), 2 deletions(-) diff --git a/libimagentrylink/src/internal.rs b/libimagentrylink/src/internal.rs index 9c2cc63e..3ce9e5f7 100644 --- a/libimagentrylink/src/internal.rs +++ b/libimagentrylink/src/internal.rs @@ -586,6 +586,7 @@ fn process_rw_result(links: StoreResult>) -> Result { } pub mod store_check { + use libimagstore::store::Store; pub mod error { generate_error_imports!(); @@ -610,7 +611,9 @@ pub mod store_check { StoreLinkConsistencyError, StoreLinkConsistencyErrorKind, StoreLinkConsistencyErrorCustomData, - StoreLinkConsistencyError => "Links in the store are not consistent" + StoreLinkConsistencyError => "Links in the store are not consistent", + LinkHandlingError => "Error in link handling", + StoreError => "Error while talking to the store" ); generate_result_helper!(StoreLinkConsistencyError, StoreLinkConsistencyErrorKind); @@ -629,8 +632,194 @@ pub mod store_check { } pub trait StoreLinkConsistentExt { + fn check_link_consistency(&self) -> Result<()>; + } + + impl StoreLinkConsistentExt for Store { fn check_link_consistency(&self) -> Result<()> { - unimplemented!() + use std::collections::HashMap; + + use self::error::StoreLinkConsistencyErrorKind as SLCEK; + use self::error::StoreLinkConsistencyError as SLCE; + use self::error::StoreLinkConsistencyErrorCustomData as SLCECD; + use error::LinkErrorKind as LEK; + use result::Result as LResult; + use internal::InternalLinker; + + use libimagstore::store::StoreObject; + use libimagstore::storeid::StoreId; + use libimagerror::iter::TraceIterator; + use libimagerror::into::IntoError; + use libimagutil::iter::FoldResult; + + // Helper data structure to collect incoming and outgoing links for each StoreId + #[derive(Debug, Default)] + struct Linking { + outgoing: Vec, + incoming: Vec, + } + + /// Helper function to aggregate the Link network + /// + /// This function aggregates a HashMap which maps each StoreId object in the store onto + /// a Linking object, which contains a list of StoreIds which this entry links to and a + /// list of StoreIds which link to the current one. + /// + /// The lambda returns an error if something fails + let aggregate_link_network = |store: &Store| -> Result> { + store + .walk("") // this is a hack... I know... + .filter_map(|obj: StoreObject| match obj { + StoreObject::Id(id) => Some(id), + _ => None + }) // Only ids are interesting + .fold(Ok(HashMap::new()), |acc, sid| { + acc.and_then(|mut state| { + debug!("Checking entry: '{}'", sid); + + match try!(self.get(sid).map_err_into(SLCEK::StoreError)) { + Some(fle) => { + debug!("Found FileLockEntry"); + + let fle_loc = fle.get_location(); + + let internal_links = fle + .get_internal_links() + .map_err_into(SLCEK::StoreError)? + .into_getter(self) // get the FLEs from the Store + .trace_unwrap(); // trace all Err(e)s and get the Ok(fle)s + + for internal_link in internal_links { + let il_loc = internal_link.get_location(); + + state + .entry(il_loc.clone()) + .or_insert(Linking::default()) + .incoming + .push(fle_loc.clone()); + + // Make sure an empty linking object is present for the + // current StoreId object + state + .entry(fle_loc.clone()) + .or_insert(Linking::default()) + .outgoing + .push(il_loc.clone()); + } + + Ok(state) + }, + None => { + debug!("No entry"); + Ok(state) + } + } + }) + }) + }; + + /// Helper to check whethre all StoreIds in the network actually exists + /// + /// Because why not? + let all_collected_storeids_exist = |network: &HashMap| -> LResult<()> { + network + .iter() + .fold_result(|(id, _)| { + if is_match!(self.get(id.clone()), Ok(Some(_))) { + debug!("Exists in store: {:?}", id); + + let exists = { + use error::MapErrInto as MEI; + try!(MEI::map_err_into(id.exists(), LEK::StoreReadError)) + }; + + if !exists { + warn!("Does exist in store but not on FS: {:?}", id); + Err(LEK::LinkTargetDoesNotExist.into_error()) + } else { + Ok(()) + } + } else { + warn!("Does not exist in store: {:?}", id); + Err(LEK::LinkTargetDoesNotExist.into_error()) + } + }) + }; + + /// Helper function to create a SLCECD::OneDirectionalLink error object + #[inline] + let mk_one_directional_link_err = |src: StoreId, target: StoreId| -> SLCE { + // construct the error + let custom = SLCECD::OneDirectionalLink { + source: src, + target: target, + }; + + SLCEK::StoreLinkConsistencyError + .into_error() + .with_custom_data(custom) + }; + + /// Helper lambda to check whether the _incoming_ links of each entry actually also + /// appear in the _outgoing_ list of the linked entry + let incoming_links_exists_as_outgoing_links = + |src: &StoreId, linking: &Linking, network: &HashMap| -> Result<()> { + linking + .incoming + .iter() + .fold_result(|link| { + + // Check whether the links which are _incoming_ on _src_ are outgoing + // in each of the links in the incoming list. + let incoming_consistent = network.get(link) + .map(|l| l.outgoing.contains(src)) + .unwrap_or(false); + + if !incoming_consistent { + Err(mk_one_directional_link_err(src.clone(), link.clone())) + } else { + Ok(()) + } + }) + }; + + /// Helper lambda to check whether the _outgoing links of each entry actually also + /// appear in the _incoming_ list of the linked entry + let outgoing_links_exist_as_incoming_links = + |src: &StoreId, linking: &Linking, network: &HashMap| -> Result<()> { + linking + .outgoing + .iter() + .fold_result(|link| { + + // Check whether the links which are _outgoing_ on _src_ are incoming + // in each of the links in the outgoing list. + let outgoing_consistent = network.get(link) + .map(|l| l.incoming.contains(src)) + .unwrap_or(false); + + if !outgoing_consistent { + Err(mk_one_directional_link_err(link.clone(), src.clone())) + } else { + Ok(()) + } + }) + }; + + aggregate_link_network(&self) + .and_then(|nw| { + all_collected_storeids_exist(&nw) + .map(|_| nw) + .map_err_into(SLCEK::LinkHandlingError) + }) + .and_then(|nw| { + nw.iter().fold_result(|(id, linking)| { + try!(incoming_links_exists_as_outgoing_links(id, linking, &nw)); + try!(outgoing_links_exist_as_incoming_links(id, linking, &nw)); + Ok(()) + }) + }) + .map(|_| ()) } } From 0155fea4c1d0b92ff7c494ca864959af49d92c96 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Tue, 6 Jun 2017 15:52:13 +0200 Subject: [PATCH 23/23] Impl Display for StoreLinkConsistencyErrorCustomData --- libimagentrylink/src/internal.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/libimagentrylink/src/internal.rs b/libimagentrylink/src/internal.rs index 3ce9e5f7..99205a3a 100644 --- a/libimagentrylink/src/internal.rs +++ b/libimagentrylink/src/internal.rs @@ -605,6 +605,23 @@ pub mod store_check { } impl Display for StoreLinkConsistencyErrorCustomData { + + fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> { + use self::StoreLinkConsistencyErrorCustomData as SLCECD; + match self { + &SLCECD::DeadLink { ref target } => { + try!(write!(fmt, "Dead Link to '{}'", target)) + }, + + &SLCECD::OneDirectionalLink { ref source, ref target } => { + try!(write!(fmt, + "Link from '{}' to '{}' does exist, but not other way round", + source, target)) + } + }; + Ok(()) + } + } generate_custom_error_types!( @@ -631,6 +648,8 @@ pub mod store_check { pub type Result = RResult; } + use self::result::Result; + pub trait StoreLinkConsistentExt { fn check_link_consistency(&self) -> Result<()>; }