use std::fmt;
use worktrunk::config::{Command, ProjectConfig};
use worktrunk::git::HookType;
#[derive(Clone)]
pub enum Phase {
Hook(HookType),
Alias,
}
impl fmt::Display for Phase {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Phase::Hook(hook_type) => write!(f, "{hook_type}"),
Phase::Alias => write!(f, "alias"),
}
}
}
#[derive(Clone)]
pub struct ApprovableCommand {
pub phase: Phase,
pub command: Command,
}
pub fn collect_commands_for_hooks(
project_config: &ProjectConfig,
hooks: &[HookType],
) -> Vec<ApprovableCommand> {
let mut commands = Vec::new();
for hook in hooks {
if let Some(config) = project_config.hooks.get(*hook) {
commands.extend(config.commands().cloned().map(|command| ApprovableCommand {
phase: Phase::Hook(*hook),
command,
}));
}
}
commands
}
pub fn collect_commands_for_aliases(project_config: &ProjectConfig) -> Vec<ApprovableCommand> {
project_config
.aliases
.iter()
.flat_map(|(alias_name, alias_cfg)| {
alias_cfg.commands().map(move |cmd| ApprovableCommand {
phase: Phase::Alias,
command: Command::new(
Some(cmd.name.clone().unwrap_or_else(|| alias_name.clone())),
cmd.template.clone(),
),
})
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
fn make_project_config_with_hooks() -> ProjectConfig {
let toml_content = r#"
post-create = "npm install"
pre-merge = "cargo test"
"#;
toml::from_str(toml_content).unwrap()
}
#[test]
fn test_collect_commands_for_hooks_empty_hooks() {
let config = make_project_config_with_hooks();
let commands = collect_commands_for_hooks(&config, &[]);
assert!(commands.is_empty());
}
#[test]
fn test_collect_commands_for_hooks_single_hook() {
let config = make_project_config_with_hooks();
let commands = collect_commands_for_hooks(&config, &[HookType::PreStart]);
assert_eq!(commands.len(), 1);
assert_eq!(commands[0].command.template, "npm install");
}
#[test]
fn test_collect_commands_for_hooks_multiple_hooks() {
let config = make_project_config_with_hooks();
let commands =
collect_commands_for_hooks(&config, &[HookType::PreStart, HookType::PreMerge]);
assert_eq!(commands.len(), 2);
assert_eq!(commands[0].command.template, "npm install");
assert_eq!(commands[1].command.template, "cargo test");
}
#[test]
fn test_collect_commands_for_hooks_missing_hook() {
let config = make_project_config_with_hooks();
let commands = collect_commands_for_hooks(&config, &[HookType::PostStart]);
assert!(commands.is_empty());
}
#[test]
fn test_collect_commands_for_hooks_order_preserved() {
let config = make_project_config_with_hooks();
let commands =
collect_commands_for_hooks(&config, &[HookType::PreMerge, HookType::PreStart]);
assert_eq!(commands.len(), 2);
assert_eq!(commands[0].command.template, "cargo test");
assert_eq!(commands[1].command.template, "npm install");
}
#[test]
fn test_collect_commands_for_hooks_all_hook_types() {
use strum::IntoEnumIterator;
let config = ProjectConfig::default();
let hooks: Vec<_> = HookType::iter().collect();
let commands = collect_commands_for_hooks(&config, &hooks);
assert!(commands.is_empty());
}
#[test]
fn test_collect_commands_for_hooks_named_commands() {
let toml_content = r#"
[post-create]
install = "npm install"
build = "npm run build"
"#;
let config: ProjectConfig = toml::from_str(toml_content).unwrap();
let commands = collect_commands_for_hooks(&config, &[HookType::PreStart]);
assert_eq!(commands.len(), 2);
assert_eq!(commands[0].command.name, Some("install".to_string()));
assert_eq!(commands[1].command.name, Some("build".to_string()));
}
#[test]
fn test_collect_commands_for_hooks_phase_is_set() {
let config = make_project_config_with_hooks();
let commands = collect_commands_for_hooks(&config, &[HookType::PreStart]);
assert_eq!(commands.len(), 1);
assert!(matches!(commands[0].phase, Phase::Hook(HookType::PreStart)));
}
}