use once_cell::sync::Lazy;
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct CommandResult {
pub is_error: bool,
pub message: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CommandType {
Default,
Grep,
Robocopy,
}
fn get_command_type(base_command: &str) -> CommandType {
match base_command {
"grep" | "rg" | "findstr" => CommandType::Grep,
"robocopy" => CommandType::Robocopy,
_ => CommandType::Default,
}
}
pub fn interpret_command_result(
command: &str,
exit_code: i32,
stdout: &str,
stderr: &str,
) -> CommandResult {
let base_command = extract_base_command(command);
let cmd_type = get_command_type(&base_command);
match cmd_type {
CommandType::Grep => {
if exit_code >= 2 {
CommandResult {
is_error: true,
message: Some(format!("Command failed with exit code {}", exit_code)),
}
} else if exit_code == 1 {
CommandResult {
is_error: false,
message: Some("No matches found".to_string()),
}
} else {
CommandResult {
is_error: false,
message: None,
}
}
}
CommandType::Robocopy => {
if exit_code >= 8 {
CommandResult {
is_error: true,
message: Some(format!("Robocopy failed with exit code {}", exit_code)),
}
} else if exit_code == 0 {
CommandResult {
is_error: false,
message: Some("No files copied (already in sync)".to_string()),
}
} else {
let message = if exit_code & 1 != 0 {
"Files copied successfully".to_string()
} else {
"Robocopy completed (no errors)".to_string()
};
CommandResult {
is_error: false,
message: Some(message),
}
}
}
CommandType::Default => {
if exit_code == 0 {
CommandResult {
is_error: false,
message: None,
}
} else {
CommandResult {
is_error: true,
message: Some(format!("Command failed with exit code {}", exit_code)),
}
}
}
}
}
fn extract_base_command(command: &str) -> String {
let segments: Vec<&str> = command
.split(|c| c == ';' || c == '|')
.filter(|s| !s.trim().is_empty())
.collect();
let last = segments.last().unwrap_or(&command);
let stripped = last.trim().trim_start_matches(&['&', '.'][..]).trim();
let first_token = stripped.split_whitespace().next().unwrap_or("");
let unquoted = first_token.trim_matches('"').trim_matches('\'');
let basename = unquoted.rsplit(|c| c == '\\' || c == '/').next().unwrap_or(unquoted);
basename.to_lowercase().trim_end_matches(".exe").to_string()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_grep_success() {
let result = interpret_command_result("grep pattern file.txt", 0, "match\n", "");
assert!(!result.is_error);
}
#[test]
fn test_grep_no_match() {
let result = interpret_command_result("grep pattern file.txt", 1, "", "");
assert!(!result.is_error);
assert_eq!(result.message, Some("No matches found".to_string()));
}
#[test]
fn test_robocopy_success() {
let result = interpret_command_result("robocopy src dst", 1, "", "");
assert!(!result.is_error);
assert_eq!(result.message, Some("Files copied successfully".to_string()));
}
#[test]
fn test_robocopy_failure() {
let result = interpret_command_result("robocopy src dst", 16, "", "");
assert!(result.is_error);
}
}