use crate::commands::CommandContext;
use crate::utils::{CommandResult, VirtualUtils};
const VIRTUAL_COMMANDS: &[&str] = &[
"echo", "pwd", "cd", "true", "false", "sleep", "cat", "ls", "mkdir", "rm", "touch", "cp", "mv",
"basename", "dirname", "env", "exit", "which", "yes", "seq", "test",
];
pub async fn which(ctx: CommandContext) -> CommandResult {
if ctx.args.is_empty() {
return VirtualUtils::missing_operand_error("which");
}
let mut output = String::new();
let mut errors = String::new();
let mut found_all = true;
for cmd in &ctx.args {
if cmd.starts_with('-') {
continue;
}
if VIRTUAL_COMMANDS.contains(&cmd.as_str()) {
output.push_str(&format!("{}: shell builtin\n", cmd));
} else {
match which::which(cmd) {
Ok(path) => {
output.push_str(&format!("{}\n", path.display()));
}
Err(_) => {
found_all = false;
errors.push_str(&format!("which: no {} in PATH\n", cmd));
}
}
}
}
if output.is_empty() {
CommandResult {
stdout: String::new(),
stderr: errors,
code: 1,
}
} else if !found_all {
CommandResult {
stdout: output,
stderr: errors,
code: 1,
}
} else {
CommandResult::success(output)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_which_existing_command() {
let ctx = CommandContext::new(vec!["sh".to_string()]);
let result = which(ctx).await;
assert!(result.code == 0 || result.code == 1);
}
#[tokio::test]
async fn test_which_nonexistent_command() {
let ctx = CommandContext::new(vec!["nonexistent_command_12345".to_string()]);
let result = which(ctx).await;
assert_eq!(result.code, 1);
}
}