command_stream/commands/
test.rs1use crate::commands::CommandContext;
4use crate::utils::CommandResult;
5use std::fs;
6use std::path::Path;
7
8pub async fn test(ctx: CommandContext) -> CommandResult {
12 if ctx.args.is_empty() {
13 return CommandResult::error_with_code("", 1);
14 }
15
16 let result = evaluate_expression(&ctx.args);
17
18 if result {
19 CommandResult::success_empty()
20 } else {
21 CommandResult::error_with_code("", 1)
22 }
23}
24
25fn evaluate_expression(args: &[String]) -> bool {
26 if args.is_empty() {
27 return false;
28 }
29
30 if args.len() == 2 {
32 let op = &args[0];
33 let arg = &args[1];
34
35 return match op.as_str() {
36 "-e" => Path::new(arg).exists(),
37 "-f" => Path::new(arg).is_file(),
38 "-d" => Path::new(arg).is_dir(),
39 "-r" => {
40 fs::metadata(arg).is_ok()
42 }
43 "-w" => {
44 fs::metadata(arg)
46 .map(|m| !m.permissions().readonly())
47 .unwrap_or(false)
48 }
49 "-x" => {
50 #[cfg(unix)]
52 {
53 use std::os::unix::fs::PermissionsExt;
54 fs::metadata(arg)
55 .map(|m| m.permissions().mode() & 0o111 != 0)
56 .unwrap_or(false)
57 }
58 #[cfg(not(unix))]
59 {
60 Path::new(arg).exists()
61 }
62 }
63 "-s" => {
64 fs::metadata(arg).map(|m| m.len() > 0).unwrap_or(false)
66 }
67 "-z" => arg.is_empty(),
68 "-n" => !arg.is_empty(),
69 "!" => !evaluate_expression(&args[1..]),
70 _ => false,
71 };
72 }
73
74 if args.len() == 3 {
76 let left = &args[0];
77 let op = &args[1];
78 let right = &args[2];
79
80 return match op.as_str() {
81 "=" | "==" => left == right,
82 "!=" => left != right,
83 "-eq" => {
84 let l: i64 = left.parse().unwrap_or(0);
85 let r: i64 = right.parse().unwrap_or(0);
86 l == r
87 }
88 "-ne" => {
89 let l: i64 = left.parse().unwrap_or(0);
90 let r: i64 = right.parse().unwrap_or(0);
91 l != r
92 }
93 "-lt" => {
94 let l: i64 = left.parse().unwrap_or(0);
95 let r: i64 = right.parse().unwrap_or(0);
96 l < r
97 }
98 "-le" => {
99 let l: i64 = left.parse().unwrap_or(0);
100 let r: i64 = right.parse().unwrap_or(0);
101 l <= r
102 }
103 "-gt" => {
104 let l: i64 = left.parse().unwrap_or(0);
105 let r: i64 = right.parse().unwrap_or(0);
106 l > r
107 }
108 "-ge" => {
109 let l: i64 = left.parse().unwrap_or(0);
110 let r: i64 = right.parse().unwrap_or(0);
111 l >= r
112 }
113 _ => false,
114 };
115 }
116
117 if args.len() == 1 {
119 return !args[0].is_empty();
120 }
121
122 false
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128 use tempfile::tempdir;
129
130 #[tokio::test]
131 async fn test_file_exists() {
132 let temp = tempdir().unwrap();
133 let file = temp.path().join("test.txt");
134 fs::write(&file, "test").unwrap();
135
136 let ctx = CommandContext::new(vec!["-e".to_string(), file.to_string_lossy().to_string()]);
137 let result = test(ctx).await;
138 assert!(result.is_success());
139 }
140
141 #[tokio::test]
142 async fn test_file_not_exists() {
143 let ctx = CommandContext::new(vec![
144 "-e".to_string(),
145 "/nonexistent/file/12345".to_string(),
146 ]);
147 let result = test(ctx).await;
148 assert!(!result.is_success());
149 }
150
151 #[tokio::test]
152 async fn test_string_equality() {
153 let ctx = CommandContext::new(vec![
154 "hello".to_string(),
155 "=".to_string(),
156 "hello".to_string(),
157 ]);
158 let result = test(ctx).await;
159 assert!(result.is_success());
160 }
161
162 #[tokio::test]
163 async fn test_string_inequality() {
164 let ctx = CommandContext::new(vec![
165 "hello".to_string(),
166 "!=".to_string(),
167 "world".to_string(),
168 ]);
169 let result = test(ctx).await;
170 assert!(result.is_success());
171 }
172
173 #[tokio::test]
174 async fn test_numeric_comparison() {
175 let ctx = CommandContext::new(vec!["5".to_string(), "-gt".to_string(), "3".to_string()]);
176 let result = test(ctx).await;
177 assert!(result.is_success());
178 }
179
180 #[tokio::test]
181 async fn test_empty_string() {
182 let ctx = CommandContext::new(vec!["-z".to_string(), "".to_string()]);
183 let result = test(ctx).await;
184 assert!(result.is_success());
185 }
186
187 #[tokio::test]
188 async fn test_non_empty_string() {
189 let ctx = CommandContext::new(vec!["-n".to_string(), "hello".to_string()]);
190 let result = test(ctx).await;
191 assert!(result.is_success());
192 }
193}