Skip to main content

command_stream/commands/
echo.rs

1//! Virtual `echo` command implementation
2
3use crate::commands::CommandContext;
4use crate::utils::CommandResult;
5
6/// Execute the echo command
7///
8/// Outputs the arguments separated by spaces, followed by a newline.
9/// Supports -n (no newline), -e (interpret escape sequences), and -E (no escape sequences)
10pub async fn echo(ctx: CommandContext) -> CommandResult {
11    let mut no_newline = false;
12    let mut interpret_escapes = false;
13    let mut args = ctx.args.iter().peekable();
14
15    // Parse flags
16    while let Some(arg) = args.peek() {
17        if arg.starts_with('-')
18            && arg.len() > 1
19            && arg
20                .chars()
21                .skip(1)
22                .all(|c| c == 'n' || c == 'e' || c == 'E')
23        {
24            let arg = args.next().unwrap();
25            for c in arg.chars().skip(1) {
26                match c {
27                    'n' => no_newline = true,
28                    'e' => interpret_escapes = true,
29                    'E' => interpret_escapes = false,
30                    _ => {}
31                }
32            }
33        } else {
34            break;
35        }
36    }
37
38    // Collect remaining args
39    let remaining: Vec<_> = args.collect();
40    let output = remaining
41        .iter()
42        .map(|s| s.as_str())
43        .collect::<Vec<_>>()
44        .join(" ");
45
46    // Process escape sequences if -e flag is set
47    let output = if interpret_escapes {
48        output
49            .replace("\\n", "\n")
50            .replace("\\t", "\t")
51            .replace("\\r", "\r")
52            .replace("\\\\", "\\")
53    } else {
54        output
55    };
56
57    if no_newline {
58        CommandResult::success(output)
59    } else {
60        CommandResult::success(format!("{}\n", output))
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67
68    #[tokio::test]
69    async fn test_echo_simple() {
70        let ctx = CommandContext::new(vec!["hello".to_string(), "world".to_string()]);
71        let result = echo(ctx).await;
72        assert!(result.is_success());
73        assert_eq!(result.stdout, "hello world\n");
74    }
75
76    #[tokio::test]
77    async fn test_echo_empty() {
78        let ctx = CommandContext::new(vec![]);
79        let result = echo(ctx).await;
80        assert!(result.is_success());
81        assert_eq!(result.stdout, "\n");
82    }
83}