libimagstorestdhook: Remove

This commit is contained in:
Matthias Beyer 2017-06-04 16:26:38 +02:00
parent dd24ce810a
commit 1b8ccb42a7
20 changed files with 0 additions and 2075 deletions

View File

@ -60,9 +60,6 @@ path = "../libimagrt"
[dependencies.libimagstore]
path = "../libimagstore"
[dependencies.libimagstorestdhook]
path = "../libimagstorestdhook"
[dependencies.libimagtimeui]
path = "../libimagtimeui"

View File

@ -31,7 +31,6 @@ members = [
"libimagref",
"libimagrt",
"libimagstore",
"libimagstorestdhook",
"libimagtimeui",
"libimagtodo",
"libimagutil",

View File

@ -1,36 +0,0 @@
[package]
name = "libimagstorestdhook"
version = "0.3.0"
authors = ["Matthias Beyer <mail@beyermatthias.de>"]
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"

View File

@ -1 +0,0 @@
../doc/src/05100-lib-store-std-hook.md

View File

@ -1,134 +0,0 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
use 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(())
}
}
}

View File

@ -1,119 +0,0 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
use 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(())
}
}
}

View File

@ -1,167 +0,0 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
use 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<File, FE> {
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(|_| ())
}
}

View File

@ -1,52 +0,0 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
#![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;

View File

@ -1,93 +0,0 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
use std::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)
}
}
}
}

View File

@ -1,69 +0,0 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
use std::fmt::{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",
})
}
}

View File

@ -1,241 +0,0 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
use 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<String> {
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<String> {
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<Option<String>> {
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<bool> {
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)
}

View File

@ -1,242 +0,0 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
use std::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<Commit> to Vec<&Commit>
let parents = parents.iter().collect::<Vec<_>>();
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(|_| ())
}
}

View File

@ -1,97 +0,0 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
use 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<T>(self, h: HEK) -> HookResult<T> {
Err(HE::new(h, Some(Box::new(self))))
}
}
impl From<GitHookError> for HE {
fn from(he: GitHookError) -> HE {
HE::new(HEK::HookExecutionError, Some(Box::new(he)))
}
}
impl From<Git2Error> for GitHookError {
fn from(ge: Git2Error) -> GitHookError {
GitHookError::new(GitHookErrorKind::RepositoryBackendError, Some(Box::new(ge)))
}
}
pub trait MapIntoHookError<T> {
fn map_into_hook_error(self) -> Result<T, HE>;
}
impl<T> MapIntoHookError<T> for Result<T, GitHookError> {
fn map_into_hook_error(self) -> Result<T, HE> {
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;

View File

@ -1,29 +0,0 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
mod action;
mod config;
pub mod delete;
mod error;
mod result;
mod runtime;
pub mod store_unload;
pub mod update;
pub mod util;

View File

@ -1,24 +0,0 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
use std::result::Result as RResult;
use vcs::git::error::GitHookError;
pub type Result<T> = RResult<T, GitHookError>;

View File

@ -1,178 +0,0 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
use std::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<Repository>,
config: Option<Value>,
}
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()))
}
}

View File

@ -1,264 +0,0 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
use std::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<Commit> to Vec<&Commit>
let parents = parents.iter().collect::<Vec<_>>();
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(|_| ())
}
}

View File

@ -1,263 +0,0 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
use std::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<Commit> to Vec<&Commit>
let parents = parents.iter().collect::<Vec<_>>();
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(|_| ())
}
}

View File

@ -1,42 +0,0 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
//! 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<Index, HookError> {
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()
}

View File

@ -1,20 +0,0 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
pub mod git;