bashkit/interpreter/
state.rs1#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
5pub enum ControlFlow {
6 #[default]
7 None,
8 Break(u32),
10 Continue(u32),
12 Return(i32),
14}
15
16#[derive(Debug, Clone, Default)]
18pub struct ExecResult {
19 pub stdout: String,
21 pub stderr: String,
23 pub exit_code: i32,
25 pub control_flow: ControlFlow,
27}
28
29impl ExecResult {
30 pub fn ok(stdout: impl Into<String>) -> Self {
32 Self {
33 stdout: stdout.into(),
34 stderr: String::new(),
35 exit_code: 0,
36 control_flow: ControlFlow::None,
37 }
38 }
39
40 pub fn err(stderr: impl Into<String>, exit_code: i32) -> Self {
42 Self {
43 stdout: String::new(),
44 stderr: stderr.into(),
45 exit_code,
46 control_flow: ControlFlow::None,
47 }
48 }
49
50 pub fn with_code(stdout: impl Into<String>, exit_code: i32) -> Self {
52 Self {
53 stdout: stdout.into(),
54 stderr: String::new(),
55 exit_code,
56 control_flow: ControlFlow::None,
57 }
58 }
59
60 pub fn with_control_flow(control_flow: ControlFlow) -> Self {
62 Self {
63 stdout: String::new(),
64 stderr: String::new(),
65 exit_code: 0,
66 control_flow,
67 }
68 }
69
70 pub fn is_success(&self) -> bool {
72 self.exit_code == 0
73 }
74}
75
76#[cfg(test)]
77#[allow(clippy::unwrap_used)]
78mod tests {
79 use super::*;
80
81 #[test]
84 fn control_flow_default_is_none() {
85 assert_eq!(ControlFlow::default(), ControlFlow::None);
86 }
87
88 #[test]
89 fn control_flow_break_stores_level() {
90 let cf = ControlFlow::Break(2);
91 assert_eq!(cf, ControlFlow::Break(2));
92 assert_ne!(cf, ControlFlow::Break(1));
93 }
94
95 #[test]
96 fn control_flow_continue_stores_level() {
97 let cf = ControlFlow::Continue(3);
98 assert_eq!(cf, ControlFlow::Continue(3));
99 }
100
101 #[test]
102 fn control_flow_return_stores_code() {
103 let cf = ControlFlow::Return(42);
104 assert_eq!(cf, ControlFlow::Return(42));
105 }
106
107 #[test]
108 fn control_flow_variants_not_equal() {
109 assert_ne!(ControlFlow::None, ControlFlow::Break(0));
110 assert_ne!(ControlFlow::Break(1), ControlFlow::Continue(1));
111 assert_ne!(ControlFlow::Continue(1), ControlFlow::Return(1));
112 }
113
114 #[test]
115 fn control_flow_clone() {
116 let cf = ControlFlow::Return(5);
117 let cloned = cf;
118 assert_eq!(cf, cloned);
119 }
120
121 #[test]
124 fn exec_result_ok_sets_stdout() {
125 let r = ExecResult::ok("hello");
126 assert_eq!(r.stdout, "hello");
127 assert_eq!(r.stderr, "");
128 assert_eq!(r.exit_code, 0);
129 assert_eq!(r.control_flow, ControlFlow::None);
130 }
131
132 #[test]
133 fn exec_result_ok_empty_string() {
134 let r = ExecResult::ok("");
135 assert_eq!(r.stdout, "");
136 assert!(r.is_success());
137 }
138
139 #[test]
140 fn exec_result_ok_accepts_string() {
141 let s = String::from("owned");
142 let r = ExecResult::ok(s);
143 assert_eq!(r.stdout, "owned");
144 }
145
146 #[test]
149 fn exec_result_err_sets_stderr_and_code() {
150 let r = ExecResult::err("bad command", 127);
151 assert_eq!(r.stdout, "");
152 assert_eq!(r.stderr, "bad command");
153 assert_eq!(r.exit_code, 127);
154 assert_eq!(r.control_flow, ControlFlow::None);
155 }
156
157 #[test]
158 fn exec_result_err_is_not_success() {
159 let r = ExecResult::err("fail", 1);
160 assert!(!r.is_success());
161 }
162
163 #[test]
164 fn exec_result_err_with_code_zero_is_success() {
165 let r = ExecResult::err("warning", 0);
167 assert!(r.is_success());
168 }
169
170 #[test]
173 fn exec_result_with_code_sets_stdout_and_code() {
174 let r = ExecResult::with_code("partial", 2);
175 assert_eq!(r.stdout, "partial");
176 assert_eq!(r.stderr, "");
177 assert_eq!(r.exit_code, 2);
178 assert_eq!(r.control_flow, ControlFlow::None);
179 }
180
181 #[test]
182 fn exec_result_with_code_zero() {
183 let r = ExecResult::with_code("ok", 0);
184 assert!(r.is_success());
185 }
186
187 #[test]
188 fn exec_result_with_code_negative() {
189 let r = ExecResult::with_code("", -1);
190 assert!(!r.is_success());
191 assert_eq!(r.exit_code, -1);
192 }
193
194 #[test]
197 fn exec_result_with_control_flow_break() {
198 let r = ExecResult::with_control_flow(ControlFlow::Break(1));
199 assert_eq!(r.stdout, "");
200 assert_eq!(r.stderr, "");
201 assert_eq!(r.exit_code, 0);
202 assert_eq!(r.control_flow, ControlFlow::Break(1));
203 }
204
205 #[test]
206 fn exec_result_with_control_flow_continue() {
207 let r = ExecResult::with_control_flow(ControlFlow::Continue(1));
208 assert_eq!(r.control_flow, ControlFlow::Continue(1));
209 }
210
211 #[test]
212 fn exec_result_with_control_flow_return() {
213 let r = ExecResult::with_control_flow(ControlFlow::Return(0));
214 assert_eq!(r.control_flow, ControlFlow::Return(0));
215 }
216
217 #[test]
218 fn exec_result_with_control_flow_none() {
219 let r = ExecResult::with_control_flow(ControlFlow::None);
220 assert_eq!(r.control_flow, ControlFlow::None);
221 assert!(r.is_success());
222 }
223
224 #[test]
227 fn exec_result_is_success_true_for_zero() {
228 let r = ExecResult::ok("x");
229 assert!(r.is_success());
230 }
231
232 #[test]
233 fn exec_result_is_success_false_for_nonzero() {
234 let r = ExecResult::err("x", 1);
235 assert!(!r.is_success());
236 let r2 = ExecResult::with_code("", 255);
237 assert!(!r2.is_success());
238 }
239
240 #[test]
243 fn exec_result_default() {
244 let r = ExecResult::default();
245 assert_eq!(r.stdout, "");
246 assert_eq!(r.stderr, "");
247 assert_eq!(r.exit_code, 0);
248 assert_eq!(r.control_flow, ControlFlow::None);
249 assert!(r.is_success());
250 }
251
252 #[test]
255 fn exec_result_debug_format() {
256 let r = ExecResult::ok("test");
257 let dbg = format!("{:?}", r);
258 assert!(dbg.contains("ExecResult"));
259 assert!(dbg.contains("test"));
260 }
261
262 #[test]
263 fn control_flow_debug_format() {
264 let cf = ControlFlow::Break(3);
265 let dbg = format!("{:?}", cf);
266 assert!(dbg.contains("Break"));
267 assert!(dbg.contains("3"));
268 }
269}