use std::collections::HashMap;
use anyhow::Result;
use conflate::Merge;
use serde::{Deserialize, Serialize};
use rustic_core::CommandInput;
#[derive(Debug, Default, Clone, Serialize, Deserialize, Merge)]
#[serde(default, rename_all = "kebab-case", deny_unknown_fields)]
pub struct Hooks {
#[merge(strategy = conflate::vec::append)]
pub run_before: Vec<CommandInput>,
#[merge(strategy = conflate::vec::append)]
pub run_after: Vec<CommandInput>,
#[merge(strategy = conflate::vec::append)]
pub run_failed: Vec<CommandInput>,
#[merge(strategy = conflate::vec::append)]
pub run_finally: Vec<CommandInput>,
#[serde(skip)]
#[merge(skip)]
pub context: String,
#[serde(skip)]
#[merge(skip)]
pub env: HashMap<String, String>,
}
impl Hooks {
pub fn with_context(&self, context: &str) -> Self {
let mut hooks = self.clone();
hooks.context = context.to_string();
hooks
}
pub fn with_env(&self, env: &HashMap<String, String>) -> Self {
let mut hooks = self.clone();
hooks.env = HashMap::<String, String>::new();
for (key, val) in env.iter() {
_ = hooks.env.insert(key.clone(), val.clone());
}
hooks
}
fn run_all(
cmds: &[CommandInput],
context: &str,
what: &str,
env: &HashMap<String, String>,
) -> Result<()> {
let mut env = env.clone();
let _ = env.insert("RUSTIC_HOOK_TYPE".to_string(), what.to_string());
for cmd in cmds {
cmd.run(context, what, &env)?;
}
Ok(())
}
pub fn run_before(&self) -> Result<()> {
Self::run_all(&self.run_before, &self.context, "run-before", &self.env)
}
pub fn run_after(&self) -> Result<()> {
Self::run_all(&self.run_after, &self.context, "run-after", &self.env)
}
pub fn run_failed(&self) -> Result<()> {
Self::run_all(&self.run_failed, &self.context, "run-failed", &self.env)
}
pub fn run_finally(&self) -> Result<()> {
Self::run_all(&self.run_finally, &self.context, "run-finally", &self.env)
}
pub fn use_with<T>(&self, f: impl FnOnce() -> Result<T>) -> Result<T> {
match self.run_before() {
Ok(()) => match f() {
Ok(result) => match self.run_after() {
Ok(()) => {
self.run_finally()?;
Ok(result)
}
Err(err_after) => {
_ = self.run_finally();
Err(err_after)
}
},
Err(err_f) => {
_ = self.run_failed();
_ = self.run_finally();
Err(err_f)
}
},
Err(err_before) => {
_ = self.run_failed();
_ = self.run_finally();
Err(err_before)
}
}
}
}