use std::collections::HashMap;
use anyhow::Result;
#[cfg(feature = "command")]
use io_process::{
command::Command,
coroutines::spawn_then_wait_with_output::{
SpawnThenWaitWithOutput, SpawnThenWaitWithOutputResult,
},
runtimes::std::handle,
};
#[cfg(any(feature = "command", feature = "notify"))]
use log::{debug, trace};
#[cfg(feature = "notify")]
use notify_rust::Notification;
use serde::{Deserialize, Serialize};
pub type Hooks = HashMap<String, Hook>;
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
pub struct Hook {
#[cfg(feature = "command")]
#[serde(alias = "cmd")]
pub command: Option<Command>,
#[cfg(feature = "notify")]
pub notify: Option<Notify>,
}
impl Hook {
pub fn exec(&self) -> Result<()> {
#[cfg(feature = "command")]
if let Some(cmd) = self.command.clone() {
debug!("execute shell command hook: {cmd:?}");
let mut arg = None;
let mut spawn = SpawnThenWaitWithOutput::new(cmd);
loop {
match spawn.resume(arg.take()) {
SpawnThenWaitWithOutputResult::Ok(_) => break,
SpawnThenWaitWithOutputResult::Io(io) => arg = Some(handle(io).unwrap()),
SpawnThenWaitWithOutputResult::Err(err) => {
debug!("error while executing shell command: {err}");
trace!("{err:?}");
}
}
}
}
#[cfg(feature = "notify")]
if let Some(notify) = self.notify.as_ref() {
debug!("execute system notification hook: {notify:?}");
let notif = Notification::new()
.summary(¬ify.summary)
.body(¬ify.body)
.show();
if let Err(err) = notif {
debug!("error while sending system notification: {err}");
trace!("{err:?}");
}
}
Ok(())
}
}
#[cfg(feature = "notify")]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
pub struct Notify {
pub summary: String,
pub body: String,
}