1use std::sync::atomic::{AtomicI32, Ordering};
15
16static LOOP_DEPTH: AtomicI32 = AtomicI32::new(0);
18
19static CONT_FLAG: AtomicI32 = AtomicI32::new(0);
21
22static BREAK_LEVEL: AtomicI32 = AtomicI32::new(0);
24
25#[derive(Debug, Clone, Default)]
27pub struct LoopState {
28 pub depth: i32,
30 pub breaks: i32,
32 pub contflag: i32,
34}
35
36impl LoopState {
37 pub fn new() -> Self {
38 Self::default()
39 }
40
41 pub fn enter(&mut self) {
43 self.depth += 1;
44 LOOP_DEPTH.store(self.depth, Ordering::Relaxed);
45 }
46
47 pub fn exit(&mut self) {
49 self.depth -= 1;
50 if self.depth < 0 {
51 self.depth = 0;
52 }
53 LOOP_DEPTH.store(self.depth, Ordering::Relaxed);
54
55 if self.breaks > 0 {
57 self.breaks -= 1;
58 }
59 if self.contflag > 0 {
60 self.contflag -= 1;
61 }
62 BREAK_LEVEL.store(self.breaks, Ordering::Relaxed);
63 CONT_FLAG.store(self.contflag, Ordering::Relaxed);
64 }
65
66 pub fn do_break(&mut self, levels: i32) {
68 self.breaks = levels.min(self.depth);
69 BREAK_LEVEL.store(self.breaks, Ordering::Relaxed);
70 }
71
72 pub fn do_continue(&mut self, levels: i32) {
74 self.contflag = levels.min(self.depth);
75 CONT_FLAG.store(self.contflag, Ordering::Relaxed);
76 }
77
78 pub fn should_break(&self) -> bool {
80 self.breaks > 0
81 }
82
83 pub fn should_continue(&self) -> bool {
85 self.contflag > 0
86 }
87
88 pub fn in_loop(&self) -> bool {
90 self.depth > 0
91 }
92
93 pub fn reset_flow(&mut self) {
95 self.contflag = 0;
96 CONT_FLAG.store(0, Ordering::Relaxed);
97 }
98
99 pub fn current_depth(&self) -> i32 {
101 self.depth
102 }
103}
104
105pub fn loop_depth() -> i32 {
107 LOOP_DEPTH.load(Ordering::Relaxed)
108}
109
110pub fn break_level() -> i32 {
112 BREAK_LEVEL.load(Ordering::Relaxed)
113}
114
115pub fn cont_flag() -> i32 {
117 CONT_FLAG.load(Ordering::Relaxed)
118}
119
120pub fn selectlist(items: &[String], prompt: &str, columns: usize) -> String {
125 let mut output = String::new();
126 let max_width = items.iter().map(|s| s.len()).max().unwrap_or(0);
127 let item_width = max_width + 4; let cols = if columns > 0 {
129 columns
130 } else {
131 let term_width = crate::utils::get_term_width();
133 (term_width / item_width.max(1)).max(1)
134 };
135
136 for (i, item) in items.iter().enumerate() {
137 let num = i + 1;
138 let entry = format!("{:>2}) {:<width$}", num, item, width = max_width);
139 output.push_str(&entry);
140
141 if (i + 1) % cols == 0 || i + 1 == items.len() {
142 output.push('\n');
143 } else {
144 output.push_str(" ");
145 }
146 }
147
148 if !prompt.is_empty() {
149 output.push_str(prompt);
150 }
151
152 output
153}
154
155pub fn select_parse_reply(reply: &str, items: &[String]) -> Option<String> {
160 let reply = reply.trim();
161 if reply.is_empty() {
162 return None;
163 }
164
165 if let Ok(n) = reply.parse::<usize>() {
167 if n >= 1 && n <= items.len() {
168 return Some(items[n - 1].clone());
169 }
170 }
171
172 None
173}
174
175pub struct ForIterator {
177 items: Vec<String>,
178 pos: usize,
179}
180
181impl ForIterator {
182 pub fn new(items: Vec<String>) -> Self {
183 ForIterator { items, pos: 0 }
184 }
185
186 pub fn from_range(start: i64, end: i64, step: i64) -> Self {
187 let mut items = Vec::new();
188 let step = if step == 0 { 1 } else { step };
189 if step > 0 {
190 let mut i = start;
191 while i <= end {
192 items.push(i.to_string());
193 i += step;
194 }
195 } else {
196 let mut i = start;
197 while i >= end {
198 items.push(i.to_string());
199 i += step;
200 }
201 }
202 ForIterator { items, pos: 0 }
203 }
204
205 pub fn len(&self) -> usize {
206 self.items.len()
207 }
208
209 pub fn is_empty(&self) -> bool {
210 self.items.is_empty()
211 }
212}
213
214impl Iterator for ForIterator {
215 type Item = String;
216
217 fn next(&mut self) -> Option<String> {
218 if self.pos < self.items.len() {
219 let item = self.items[self.pos].clone();
220 self.pos += 1;
221 Some(item)
222 } else {
223 None
224 }
225 }
226}
227
228pub struct CForState {
230 pub init_done: bool,
231}
232
233impl CForState {
234 pub fn new() -> Self {
235 CForState { init_done: false }
236 }
237}
238
239impl Default for CForState {
240 fn default() -> Self {
241 Self::new()
242 }
243}
244
245#[derive(Debug, Clone, Default)]
247pub struct TryState {
248 pub in_try: bool,
249 pub try_errflag: i32,
250 pub try_retval: i32,
251}
252
253impl TryState {
254 pub fn new() -> Self {
255 Self::default()
256 }
257
258 pub fn enter_try(&mut self) {
259 self.in_try = true;
260 self.try_errflag = 0;
261 self.try_retval = 0;
262 }
263
264 pub fn exit_try(&mut self) {
265 self.in_try = false;
266 }
267
268 pub fn set_error(&mut self, errflag: i32, retval: i32) {
269 self.try_errflag = errflag;
270 self.try_retval = retval;
271 }
272}
273
274#[cfg(test)]
275mod tests {
276 use super::*;
277
278 #[test]
279 fn test_loop_state() {
280 let mut state = LoopState::new();
281 assert!(!state.in_loop());
282
283 state.enter();
284 assert!(state.in_loop());
285 assert_eq!(state.current_depth(), 1);
286
287 state.enter();
288 assert_eq!(state.current_depth(), 2);
289
290 state.exit();
291 assert_eq!(state.current_depth(), 1);
292 assert!(state.in_loop());
293
294 state.exit();
295 assert!(!state.in_loop());
296 }
297
298 #[test]
299 fn test_break_continue() {
300 let mut state = LoopState::new();
301 state.enter();
302 state.enter();
303
304 state.do_break(1);
305 assert!(state.should_break());
306
307 state.exit();
308 assert!(!state.should_break());
309 }
310
311 #[test]
312 fn test_for_iterator() {
313 let iter = ForIterator::new(vec!["a".into(), "b".into(), "c".into()]);
314 let items: Vec<String> = iter.collect();
315 assert_eq!(items, vec!["a", "b", "c"]);
316 }
317
318 #[test]
319 fn test_for_range() {
320 let iter = ForIterator::from_range(1, 5, 1);
321 let items: Vec<String> = iter.collect();
322 assert_eq!(items, vec!["1", "2", "3", "4", "5"]);
323 }
324
325 #[test]
326 fn test_select_parse() {
327 let items = vec!["apple".into(), "banana".into(), "cherry".into()];
328 assert_eq!(select_parse_reply("1", &items), Some("apple".to_string()));
329 assert_eq!(select_parse_reply("3", &items), Some("cherry".to_string()));
330 assert_eq!(select_parse_reply("0", &items), None);
331 assert_eq!(select_parse_reply("4", &items), None);
332 assert_eq!(select_parse_reply("", &items), None);
333 }
334
335 #[test]
336 fn test_selectlist() {
337 let items = vec!["one".into(), "two".into(), "three".into()];
338 let output = selectlist(&items, "? ", 0);
339 assert!(output.contains("1)"));
340 assert!(output.contains("one"));
341 assert!(output.contains("three"));
342 }
343
344 #[test]
345 fn test_try_state() {
346 let mut state = TryState::new();
347 assert!(!state.in_try);
348
349 state.enter_try();
350 assert!(state.in_try);
351
352 state.set_error(1, 42);
353 assert_eq!(state.try_errflag, 1);
354 assert_eq!(state.try_retval, 42);
355
356 state.exit_try();
357 assert!(!state.in_try);
358 }
359}