use std::{collections::HashMap, sync::Arc};
use serde::{Deserialize, Serialize};
use crate::config_build;
fn deserialize_string_or_vec<'de, D>(deserializer: D) -> Result<Arc<Vec<String>>, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::{self, Deserialize};
struct StringOrVec;
impl<'de> serde::de::Visitor<'de> for StringOrVec {
type Value = Arc<Vec<String>>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("string or array of strings")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Arc::new(vec![value.to_string()]))
}
fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'de>,
{
let vec = Vec::<String>::deserialize(de::value::SeqAccessDeserializer::new(seq))?;
Ok(Arc::new(vec))
}
}
deserializer.deserialize_any(StringOrVec)
}
config_build! {
HooksConfig<crate::cli::commands::hooks::HooksArgs> {
skip_all: bool => {
cli: |args: &crate::cli::commands::hooks::HooksArgs| args.skip_all,
env: "GUARDY_HOOKS_SKIP_ALL",
default: false,
},
parallel: bool => {
cli: |args: &crate::cli::commands::hooks::HooksArgs| args.parallel,
env: "GUARDY_HOOKS_PARALLEL",
default: true,
},
continue_on_error: bool => {
env: "GUARDY_HOOKS_CONTINUE_ON_ERROR",
default: false,
},
auto_stage: bool => {
env: "GUARDY_HOOKS_AUTO_STAGE",
default: false,
},
pre_commit: Arc<HookDefinition> => {
default: Arc::new(HookDefinition::default()),
},
commit_msg: Arc<HookDefinition> => {
default: Arc::new(HookDefinition::default()),
},
pre_push: Arc<HookDefinition> => {
default: Arc::new(HookDefinition::default()),
},
post_checkout: Arc<HookDefinition> => {
default: Arc::new(HookDefinition::default()),
},
post_commit: Arc<HookDefinition> => {
default: Arc::new(HookDefinition::default()),
},
post_merge: Arc<HookDefinition> => {
default: Arc::new(HookDefinition::default()),
},
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "kebab-case")]
pub struct HookDefinition {
#[serde(default)]
pub parallel: bool,
#[serde(default)]
pub skip: bool,
#[serde(default)]
pub builtin: Vec<String>,
#[serde(default)]
pub commands: HashMap<String, HookCommand>,
#[serde(default)]
pub scripts: HashMap<String, HookScript>,
#[serde(default, rename = "conventional-commits")]
pub conventional_commits: Option<ConventionalCommitsConfig>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "kebab-case")]
pub struct ConventionalCommitsConfig {
#[serde(default)]
pub allowed_types: Option<Vec<String>>,
#[serde(default)]
pub enforce_scope: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "kebab-case")]
pub struct HookCommand {
pub run: String,
#[serde(default)]
pub description: String,
#[serde(default)]
pub continue_on_error: bool,
#[serde(default)]
pub all_files: bool,
#[serde(default, deserialize_with = "deserialize_string_or_vec")]
pub glob: Arc<Vec<String>>,
#[serde(default)]
pub file_types: Vec<String>,
#[serde(default)]
pub stage_fixed: bool,
#[serde(default)]
pub skip: HookCondition,
#[serde(default)]
pub only: HookCondition,
#[serde(default)]
pub env: HashMap<String, String>,
#[serde(default)]
pub root: Option<String>,
#[serde(default)]
pub exclude: Vec<String>,
#[serde(default)]
pub priority: i32,
#[serde(default)]
pub fail_text: Option<String>,
#[serde(default)]
pub interactive: bool,
#[serde(default)]
pub use_stdin: bool,
#[serde(default)]
pub tags: Vec<String>,
#[serde(default)]
pub files: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HookScript {
pub runner: String,
#[serde(default)]
pub env: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum HookCondition {
Bool(bool),
Array(Vec<String>),
}
impl Default for HookCondition {
fn default() -> Self {
Self::Bool(false)
}
}