Skip to main content

command_stream/commands/
seq.rs

1//! Virtual `seq` command implementation
2
3use crate::commands::CommandContext;
4use crate::utils::{CommandResult, VirtualUtils};
5
6/// Execute the seq command
7///
8/// Prints a sequence of numbers.
9pub async fn seq(ctx: CommandContext) -> CommandResult {
10    if ctx.args.is_empty() {
11        return VirtualUtils::missing_operand_error("seq");
12    }
13
14    let (first, increment, last) = match ctx.args.len() {
15        1 => {
16            let last: f64 = match ctx.args[0].parse() {
17                Ok(n) => n,
18                Err(_) => {
19                    return CommandResult::error(format!(
20                        "seq: invalid floating point argument: '{}'\n",
21                        ctx.args[0]
22                    ));
23                }
24            };
25            (1.0, 1.0, last)
26        }
27        2 => {
28            let first: f64 = match ctx.args[0].parse() {
29                Ok(n) => n,
30                Err(_) => {
31                    return CommandResult::error(format!(
32                        "seq: invalid floating point argument: '{}'\n",
33                        ctx.args[0]
34                    ));
35                }
36            };
37            let last: f64 = match ctx.args[1].parse() {
38                Ok(n) => n,
39                Err(_) => {
40                    return CommandResult::error(format!(
41                        "seq: invalid floating point argument: '{}'\n",
42                        ctx.args[1]
43                    ));
44                }
45            };
46            (first, 1.0, last)
47        }
48        _ => {
49            let first: f64 = match ctx.args[0].parse() {
50                Ok(n) => n,
51                Err(_) => {
52                    return CommandResult::error(format!(
53                        "seq: invalid floating point argument: '{}'\n",
54                        ctx.args[0]
55                    ));
56                }
57            };
58            let increment: f64 = match ctx.args[1].parse() {
59                Ok(n) => n,
60                Err(_) => {
61                    return CommandResult::error(format!(
62                        "seq: invalid floating point argument: '{}'\n",
63                        ctx.args[1]
64                    ));
65                }
66            };
67            let last: f64 = match ctx.args[2].parse() {
68                Ok(n) => n,
69                Err(_) => {
70                    return CommandResult::error(format!(
71                        "seq: invalid floating point argument: '{}'\n",
72                        ctx.args[2]
73                    ));
74                }
75            };
76            (first, increment, last)
77        }
78    };
79
80    if increment == 0.0 {
81        return CommandResult::error("seq: zero increment\n");
82    }
83
84    let mut output = String::new();
85    let mut current = first;
86
87    if increment > 0.0 {
88        while current <= last {
89            if ctx.is_cancelled() {
90                return CommandResult::error_with_code("", 130);
91            }
92
93            // Format as integer if possible
94            if current.fract() == 0.0 {
95                output.push_str(&format!("{}\n", current as i64));
96            } else {
97                output.push_str(&format!("{}\n", current));
98            }
99            current += increment;
100        }
101    } else {
102        while current >= last {
103            if ctx.is_cancelled() {
104                return CommandResult::error_with_code("", 130);
105            }
106
107            if current.fract() == 0.0 {
108                output.push_str(&format!("{}\n", current as i64));
109            } else {
110                output.push_str(&format!("{}\n", current));
111            }
112            current += increment;
113        }
114    }
115
116    CommandResult::success(output)
117}
118
119#[cfg(test)]
120mod tests {
121    use super::*;
122
123    #[tokio::test]
124    async fn test_seq_single_arg() {
125        let ctx = CommandContext::new(vec!["5".to_string()]);
126        let result = seq(ctx).await;
127
128        assert!(result.is_success());
129        assert_eq!(result.stdout, "1\n2\n3\n4\n5\n");
130    }
131
132    #[tokio::test]
133    async fn test_seq_two_args() {
134        let ctx = CommandContext::new(vec!["3".to_string(), "7".to_string()]);
135        let result = seq(ctx).await;
136
137        assert!(result.is_success());
138        assert_eq!(result.stdout, "3\n4\n5\n6\n7\n");
139    }
140
141    #[tokio::test]
142    async fn test_seq_three_args() {
143        let ctx = CommandContext::new(vec!["2".to_string(), "2".to_string(), "8".to_string()]);
144        let result = seq(ctx).await;
145
146        assert!(result.is_success());
147        assert_eq!(result.stdout, "2\n4\n6\n8\n");
148    }
149
150    #[tokio::test]
151    async fn test_seq_descending() {
152        let ctx = CommandContext::new(vec!["5".to_string(), "-1".to_string(), "1".to_string()]);
153        let result = seq(ctx).await;
154
155        assert!(result.is_success());
156        assert_eq!(result.stdout, "5\n4\n3\n2\n1\n");
157    }
158
159    #[tokio::test]
160    async fn test_seq_zero_increment() {
161        let ctx = CommandContext::new(vec!["1".to_string(), "0".to_string(), "5".to_string()]);
162        let result = seq(ctx).await;
163
164        assert!(!result.is_success());
165        assert!(result.stderr.contains("zero increment"));
166    }
167}