use std::path::PathBuf;
use std::time::Duration;
#[derive(Debug, Clone)]
pub struct HookContext {
pub worktree_path: PathBuf,
pub branch_name: String,
pub repo_root: PathBuf,
pub repo_name: String,
}
#[derive(Debug, Clone)]
pub struct HookExecutionDetail {
pub command: String,
pub success: bool,
pub duration: Duration,
pub exit_code: Option<i32>,
pub error_message: Option<String>,
}
impl HookExecutionDetail {
pub fn success(command: String, duration: Duration) -> Self {
Self {
command,
success: true,
duration,
exit_code: Some(0),
error_message: None,
}
}
pub fn failure_with_code(command: String, duration: Duration, exit_code: i32) -> Self {
Self {
command,
success: false,
duration,
exit_code: Some(exit_code),
error_message: Some(format!("Exit code: {}", exit_code)),
}
}
pub fn failure_with_error(command: String, duration: Duration, error: String) -> Self {
Self {
command,
success: false,
duration,
exit_code: Some(1),
error_message: Some(error),
}
}
}
#[derive(Debug, Clone)]
pub struct HookResult {
pub success: bool,
pub executed_count: usize,
pub failed_command: Option<String>,
pub exit_code: Option<i32>,
pub total_duration: Duration,
pub details: Vec<HookExecutionDetail>,
}
impl HookResult {
pub fn success(
executed_count: usize,
total_duration: Duration,
details: Vec<HookExecutionDetail>,
) -> Self {
Self {
success: true,
executed_count,
failed_command: None,
exit_code: None,
total_duration,
details,
}
}
pub fn failure(
executed_count: usize,
failed_command: String,
exit_code: i32,
total_duration: Duration,
details: Vec<HookExecutionDetail>,
) -> Self {
Self {
success: false,
executed_count,
failed_command: Some(failed_command),
exit_code: Some(exit_code),
total_duration,
details,
}
}
pub fn no_hooks() -> Self {
Self {
success: true,
executed_count: 0,
failed_command: None,
exit_code: None,
total_duration: Duration::ZERO,
details: Vec::new(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hook_result_success() {
let details = vec![
HookExecutionDetail {
command: "npm install".to_string(),
success: true,
duration: Duration::from_millis(1500),
exit_code: Some(0),
error_message: None,
},
HookExecutionDetail {
command: "npm run build".to_string(),
success: true,
duration: Duration::from_millis(800),
exit_code: Some(0),
error_message: None,
},
];
let result = HookResult::success(2, Duration::from_millis(2300), details);
assert!(result.success);
assert_eq!(result.executed_count, 2);
assert!(result.failed_command.is_none());
assert!(result.exit_code.is_none());
assert_eq!(result.total_duration, Duration::from_millis(2300));
assert_eq!(result.details.len(), 2);
}
#[test]
fn test_hook_result_failure() {
let details = vec![
HookExecutionDetail {
command: "npm install".to_string(),
success: true,
duration: Duration::from_millis(1500),
exit_code: Some(0),
error_message: None,
},
HookExecutionDetail {
command: "npm run build".to_string(),
success: false,
duration: Duration::from_millis(200),
exit_code: Some(1),
error_message: Some("Build failed".to_string()),
},
];
let result = HookResult::failure(
2,
"npm run build".to_string(),
1,
Duration::from_millis(1700),
details,
);
assert!(!result.success);
assert_eq!(result.executed_count, 2);
assert_eq!(result.failed_command, Some("npm run build".to_string()));
assert_eq!(result.exit_code, Some(1));
assert_eq!(result.total_duration, Duration::from_millis(1700));
assert_eq!(result.details.len(), 2);
}
#[test]
fn test_hook_result_no_hooks() {
let result = HookResult::no_hooks();
assert!(result.success);
assert_eq!(result.executed_count, 0);
assert!(result.failed_command.is_none());
assert!(result.exit_code.is_none());
assert_eq!(result.total_duration, Duration::ZERO);
assert!(result.details.is_empty());
}
#[test]
fn test_hook_execution_detail() {
let detail = HookExecutionDetail {
command: "echo hello".to_string(),
success: true,
duration: Duration::from_millis(50),
exit_code: Some(0),
error_message: None,
};
assert_eq!(detail.command, "echo hello");
assert!(detail.success);
assert_eq!(detail.duration, Duration::from_millis(50));
}
#[test]
fn test_hook_execution_detail_success_helper() {
let detail =
HookExecutionDetail::success("npm install".to_string(), Duration::from_millis(1500));
assert_eq!(detail.command, "npm install");
assert!(detail.success);
assert_eq!(detail.exit_code, Some(0));
assert!(detail.error_message.is_none());
}
#[test]
fn test_hook_execution_detail_failure_with_code() {
let detail = HookExecutionDetail::failure_with_code(
"npm run build".to_string(),
Duration::from_millis(200),
1,
);
assert_eq!(detail.command, "npm run build");
assert!(!detail.success);
assert_eq!(detail.exit_code, Some(1));
assert!(detail.error_message.is_some());
}
#[test]
fn test_hook_execution_detail_failure_with_error() {
let detail = HookExecutionDetail::failure_with_error(
"invalid_cmd".to_string(),
Duration::from_millis(10),
"Command not found".to_string(),
);
assert_eq!(detail.command, "invalid_cmd");
assert!(!detail.success);
assert_eq!(detail.exit_code, Some(1));
assert_eq!(detail.error_message, Some("Command not found".to_string()));
}
}