use crate::hooks::HookEvent;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct PluginConfig {
pub enabled: bool,
pub hook_timeout_ms: u64,
pub paths: Vec<String>,
pub continue_on_error: bool,
#[serde(default)]
pub hooks: HookConfig,
#[serde(skip)]
pub trust_local: bool,
}
impl PluginConfig {
pub fn new() -> Self {
PluginConfig {
enabled: true,
hook_timeout_ms: 5000,
paths: vec![],
continue_on_error: false,
hooks: HookConfig::default(),
trust_local: false,
}
}
pub fn local_plugins_dir(project_root: &std::path::Path) -> PathBuf {
project_root.join(".hx").join("plugins")
}
pub fn hook_timeout(&self) -> std::time::Duration {
std::time::Duration::from_millis(self.hook_timeout_ms)
}
pub fn all_paths(&self, project_root: &std::path::Path) -> Vec<PathBuf> {
let mut paths = Vec::new();
if self.trust_local {
paths.push(Self::local_plugins_dir(project_root));
}
for path in &self.paths {
let expanded = shellexpand::tilde(path);
paths.push(PathBuf::from(expanded.as_ref()));
}
if let Some(config_dir) = dirs::config_dir() {
paths.push(config_dir.join("hx").join("plugins"));
}
paths
}
pub fn scripts_for_hook(&self, event: HookEvent) -> &[String] {
match event {
HookEvent::PreBuild => &self.hooks.pre_build,
HookEvent::PostBuild => &self.hooks.post_build,
HookEvent::PreTest => &self.hooks.pre_test,
HookEvent::PostTest => &self.hooks.post_test,
HookEvent::PreRun => &self.hooks.pre_run,
HookEvent::PostRun => &self.hooks.post_run,
HookEvent::PreClean => &self.hooks.pre_clean,
HookEvent::PostClean => &self.hooks.post_clean,
HookEvent::PreLock => &self.hooks.pre_lock,
HookEvent::PostLock => &self.hooks.post_lock,
HookEvent::Init => &self.hooks.init,
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct HookConfig {
pub pre_build: Vec<String>,
pub post_build: Vec<String>,
pub pre_test: Vec<String>,
pub post_test: Vec<String>,
pub pre_run: Vec<String>,
pub post_run: Vec<String>,
pub pre_clean: Vec<String>,
pub post_clean: Vec<String>,
pub pre_lock: Vec<String>,
pub post_lock: Vec<String>,
pub init: Vec<String>,
}
impl HookConfig {
pub fn has_any_hooks(&self) -> bool {
!self.pre_build.is_empty()
|| !self.post_build.is_empty()
|| !self.pre_test.is_empty()
|| !self.post_test.is_empty()
|| !self.pre_run.is_empty()
|| !self.post_run.is_empty()
|| !self.pre_clean.is_empty()
|| !self.post_clean.is_empty()
|| !self.pre_lock.is_empty()
|| !self.post_lock.is_empty()
|| !self.init.is_empty()
}
pub fn as_map(&self) -> HashMap<HookEvent, &[String]> {
let mut map = HashMap::new();
map.insert(HookEvent::PreBuild, self.pre_build.as_slice());
map.insert(HookEvent::PostBuild, self.post_build.as_slice());
map.insert(HookEvent::PreTest, self.pre_test.as_slice());
map.insert(HookEvent::PostTest, self.post_test.as_slice());
map.insert(HookEvent::PreRun, self.pre_run.as_slice());
map.insert(HookEvent::PostRun, self.post_run.as_slice());
map.insert(HookEvent::PreClean, self.pre_clean.as_slice());
map.insert(HookEvent::PostClean, self.post_clean.as_slice());
map.insert(HookEvent::PreLock, self.pre_lock.as_slice());
map.insert(HookEvent::PostLock, self.post_lock.as_slice());
map.insert(HookEvent::Init, self.init.as_slice());
map
}
}
mod dirs {
use std::path::PathBuf;
pub fn config_dir() -> Option<PathBuf> {
directories::BaseDirs::new().map(|dirs| dirs.config_dir().to_path_buf())
}
}
mod shellexpand {
use std::borrow::Cow;
pub fn tilde(path: &str) -> Cow<'_, str> {
if path.starts_with("~/")
&& let Some(home) = directories::BaseDirs::new()
{
let home_str = home.home_dir().to_string_lossy();
return Cow::Owned(format!("{}{}", home_str, &path[1..]));
}
Cow::Borrowed(path)
}
}
impl From<hx_config::PluginConfig> for PluginConfig {
fn from(config: hx_config::PluginConfig) -> Self {
PluginConfig {
enabled: config.enabled,
hook_timeout_ms: config.hook_timeout_ms,
paths: config.paths,
continue_on_error: config.continue_on_error,
hooks: HookConfig::from(config.hooks),
trust_local: false,
}
}
}
impl From<hx_config::PluginHookConfig> for HookConfig {
fn from(hooks: hx_config::PluginHookConfig) -> Self {
HookConfig {
pre_build: hooks.pre_build,
post_build: hooks.post_build,
pre_test: hooks.pre_test,
post_test: hooks.post_test,
pre_run: hooks.pre_run,
post_run: hooks.post_run,
pre_clean: hooks.pre_clean,
post_clean: hooks.post_clean,
pre_lock: hooks.pre_lock,
post_lock: hooks.post_lock,
init: hooks.init,
}
}
}