command_stream/commands/
seq.rs1use crate::commands::CommandContext;
4use crate::utils::{CommandResult, VirtualUtils};
5
6pub 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 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}