sorting_race/lib/
input.rs

1//! Input handling for the terminal interface
2
3use std::io::{self, Read, Write};
4
5/// Input handler for terminal interface
6#[derive(Debug, Default)]
7pub struct InputHandler {
8    buffer: String,
9}
10
11impl InputHandler {
12    /// Create a new input handler
13    pub fn new() -> Self {
14        Self::default()
15    }
16
17    /// Read a line of input from stdin
18    pub fn read_line(&mut self) -> io::Result<String> {
19        self.buffer.clear();
20        io::stdin().read_line(&mut self.buffer)?;
21        Ok(self.buffer.trim().to_string())
22    }
23
24    /// Read a single character without waiting for Enter
25    pub fn read_char(&mut self) -> io::Result<char> {
26        let mut buffer = [0; 1];
27        io::stdin().read_exact(&mut buffer)?;
28        Ok(buffer[0] as char)
29    }
30
31    /// Prompt user with a message and read input
32    pub fn prompt(&mut self, message: &str) -> io::Result<String> {
33        print!("{}", message);
34        io::stdout().flush()?;
35        self.read_line()
36    }
37
38    /// Read a number from input with validation
39    pub fn read_number<T>(&mut self, prompt: &str) -> io::Result<T>
40    where
41        T: std::str::FromStr,
42        T::Err: std::fmt::Display,
43    {
44        loop {
45            let input = self.prompt(prompt)?;
46            match input.parse::<T>() {
47                Ok(value) => return Ok(value),
48                Err(e) => {
49                    println!("Invalid input: {}. Please try again.", e);
50                }
51            }
52        }
53    }
54
55    /// Read a yes/no answer from input
56    pub fn read_yes_no(&mut self, prompt: &str) -> io::Result<bool> {
57        loop {
58            let input = self.prompt(&format!("{} (y/n): ", prompt))?;
59            match input.to_lowercase().as_str() {
60                "y" | "yes" | "true" | "1" => return Ok(true),
61                "n" | "no" | "false" | "0" => return Ok(false),
62                _ => println!("Please enter 'y' for yes or 'n' for no."),
63            }
64        }
65    }
66
67    /// Read input with validation using a predicate function
68    pub fn read_validated<F>(&mut self, prompt: &str, validator: F) -> io::Result<String>
69    where
70        F: Fn(&str) -> Result<(), String>,
71    {
72        loop {
73            let input = self.prompt(prompt)?;
74            match validator(&input) {
75                Ok(()) => return Ok(input),
76                Err(error) => {
77                    println!("Invalid input: {}. Please try again.", error);
78                }
79            }
80        }
81    }
82
83    /// Read input from a list of choices
84    pub fn read_choice(&mut self, prompt: &str, choices: &[&str]) -> io::Result<usize> {
85        loop {
86            println!("{}", prompt);
87            for (i, choice) in choices.iter().enumerate() {
88                println!("{}. {}", i + 1, choice);
89            }
90
91            let input = self.prompt("Enter your choice (number): ")?;
92            match input.parse::<usize>() {
93                Ok(choice) if choice > 0 && choice <= choices.len() => {
94                    return Ok(choice - 1);
95                }
96                _ => {
97                    println!(
98                        "Please enter a number between 1 and {}.",
99                        choices.len()
100                    );
101                }
102            }
103        }
104    }
105
106    /// Clear the input buffer
107    pub fn clear_buffer(&mut self) {
108        self.buffer.clear();
109    }
110}
111
112/// Key codes for special keys
113#[derive(Debug, Clone, Copy, PartialEq, Eq)]
114pub enum KeyCode {
115    Char(char),
116    Enter,
117    Escape,
118    Space,
119    Backspace,
120    Tab,
121    Up,
122    Down,
123    Left,
124    Right,
125    Home,
126    End,
127    PageUp,
128    PageDown,
129    Delete,
130    Insert,
131    F1,
132    F2,
133    F3,
134    F4,
135    F5,
136    F6,
137    F7,
138    F8,
139    F9,
140    F10,
141    F11,
142    F12,
143}
144
145/// Event representing user input
146#[derive(Debug, Clone, PartialEq, Eq)]
147pub enum InputEvent {
148    Key(KeyCode),
149    Resize(u16, u16), // width, height
150    Mouse(u16, u16),  // x, y
151}
152
153/// Non-blocking input reader (stub implementation)
154#[derive(Debug)]
155pub struct NonBlockingInput {
156    _private: (),
157}
158
159impl NonBlockingInput {
160    /// Create a new non-blocking input reader
161    pub fn new() -> io::Result<Self> {
162        Ok(Self { _private: () })
163    }
164
165    /// Poll for input events (stub implementation)
166    pub fn poll_event(&mut self) -> io::Result<Option<InputEvent>> {
167        // This is a stub implementation
168        // In a real implementation, this would use platform-specific code
169        // to read input without blocking
170        Ok(None)
171    }
172
173    /// Check if input is available
174    pub fn has_input(&self) -> bool {
175        // Stub implementation
176        false
177    }
178}
179
180impl Default for NonBlockingInput {
181    fn default() -> Self {
182        Self::new().unwrap()
183    }
184}
185
186/// Menu system for terminal interface
187#[derive(Debug)]
188pub struct Menu {
189    title: String,
190    items: Vec<String>,
191    selected_index: usize,
192}
193
194impl Menu {
195    /// Create a new menu
196    pub fn new(title: String, items: Vec<String>) -> Self {
197        Self {
198            title,
199            items,
200            selected_index: 0,
201        }
202    }
203
204    /// Display the menu and get user selection
205    pub fn show(&mut self, input_handler: &mut InputHandler) -> io::Result<usize> {
206        loop {
207            // Clear screen (simple implementation)
208            print!("\x1B[2J\x1B[H");
209            
210            // Display title
211            println!("{}", self.title);
212            println!("{}", "=".repeat(self.title.len()));
213            println!();
214
215            // Display items
216            for (i, item) in self.items.iter().enumerate() {
217                if i == self.selected_index {
218                    println!("> {}", item);
219                } else {
220                    println!("  {}", item);
221                }
222            }
223
224            println!();
225            println!("Use numbers to select, or 'q' to quit:");
226
227            let input = input_handler.read_line()?;
228            
229            if input == "q" || input == "quit" {
230                return Err(io::Error::new(io::ErrorKind::Interrupted, "User quit"));
231            }
232
233            match input.parse::<usize>() {
234                Ok(choice) if choice > 0 && choice <= self.items.len() => {
235                    return Ok(choice - 1);
236                }
237                _ => {
238                    println!("Please enter a number between 1 and {} or 'q' to quit.", self.items.len());
239                    println!("Press Enter to continue...");
240                    input_handler.read_line()?;
241                }
242            }
243        }
244    }
245
246    /// Set the selected index
247    pub fn set_selected(&mut self, index: usize) {
248        if index < self.items.len() {
249            self.selected_index = index;
250        }
251    }
252
253    /// Get the selected index
254    pub fn get_selected(&self) -> usize {
255        self.selected_index
256    }
257
258    /// Add an item to the menu
259    pub fn add_item(&mut self, item: String) {
260        self.items.push(item);
261    }
262
263    /// Remove an item from the menu
264    pub fn remove_item(&mut self, index: usize) -> Option<String> {
265        if index < self.items.len() {
266            Some(self.items.remove(index))
267        } else {
268            None
269        }
270    }
271
272    /// Get the number of items
273    pub fn len(&self) -> usize {
274        self.items.len()
275    }
276
277    /// Check if menu is empty
278    pub fn is_empty(&self) -> bool {
279        self.items.is_empty()
280    }
281}
282
283#[cfg(test)]
284mod tests {
285    use super::*;
286
287    #[test]
288    fn test_input_handler_creation() {
289        let handler = InputHandler::new();
290        assert_eq!(handler.buffer.len(), 0);
291    }
292
293    #[test]
294    fn test_non_blocking_input_creation() {
295        let input = NonBlockingInput::new();
296        assert!(input.is_ok());
297    }
298
299    #[test]
300    fn test_menu_creation() {
301        let menu = Menu::new(
302            "Test Menu".to_string(),
303            vec!["Option 1".to_string(), "Option 2".to_string()],
304        );
305        assert_eq!(menu.len(), 2);
306        assert_eq!(menu.get_selected(), 0);
307    }
308
309    #[test]
310    fn test_menu_selection() {
311        let mut menu = Menu::new(
312            "Test Menu".to_string(),
313            vec!["Option 1".to_string(), "Option 2".to_string()],
314        );
315        menu.set_selected(1);
316        assert_eq!(menu.get_selected(), 1);
317    }
318
319    #[test]
320    fn test_menu_add_remove() {
321        let mut menu = Menu::new("Test".to_string(), vec![]);
322        menu.add_item("Item 1".to_string());
323        assert_eq!(menu.len(), 1);
324        
325        let removed = menu.remove_item(0);
326        assert_eq!(removed, Some("Item 1".to_string()));
327        assert_eq!(menu.len(), 0);
328    }
329}