1use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum KeyAction {
10 InsertChar(char),
12 Backspace,
14 Delete,
16 CursorLeft,
18 CursorRight,
20 CursorHome,
22 CursorEnd,
24 Evaluate,
26 Clear,
28 ClearAll,
30 RecallLast,
32 Quit,
34 None,
36}
37
38#[derive(Debug, Default)]
40pub struct InputHandler;
41
42impl InputHandler {
43 #[must_use]
45 pub fn new() -> Self {
46 Self
47 }
48
49 #[must_use]
51 pub fn handle_key(&self, event: KeyEvent) -> KeyAction {
52 let KeyEvent {
53 code, modifiers, ..
54 } = event;
55
56 if modifiers.contains(KeyModifiers::CONTROL) {
58 return match code {
59 KeyCode::Char('c' | 'q') => KeyAction::Quit,
60 KeyCode::Char('l') => KeyAction::ClearAll,
61 KeyCode::Char('a') => KeyAction::CursorHome,
62 KeyCode::Char('e') => KeyAction::CursorEnd,
63 KeyCode::Char('u') => KeyAction::Clear,
64 _ => KeyAction::None,
65 };
66 }
67
68 match code {
70 KeyCode::Char(c) => KeyAction::InsertChar(c),
71 KeyCode::Backspace => KeyAction::Backspace,
72 KeyCode::Delete => KeyAction::Delete,
73 KeyCode::Left => KeyAction::CursorLeft,
74 KeyCode::Right => KeyAction::CursorRight,
75 KeyCode::Home => KeyAction::CursorHome,
76 KeyCode::End => KeyAction::CursorEnd,
77 KeyCode::Enter => KeyAction::Evaluate,
78 KeyCode::Esc => KeyAction::Clear,
79 KeyCode::Up => KeyAction::RecallLast,
80 _ => KeyAction::None,
81 }
82 }
83
84 #[must_use]
86 pub fn is_valid_char(c: char) -> bool {
87 c.is_ascii_digit()
88 || c == '.'
89 || c == '+'
90 || c == '-'
91 || c == '*'
92 || c == '/'
93 || c == '%'
94 || c == '^'
95 || c == '('
96 || c == ')'
97 || c == ' '
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104
105 fn key_event(code: KeyCode) -> KeyEvent {
106 KeyEvent::new(code, KeyModifiers::NONE)
107 }
108
109 fn key_event_ctrl(code: KeyCode) -> KeyEvent {
110 KeyEvent::new(code, KeyModifiers::CONTROL)
111 }
112
113 #[test]
116 fn test_input_handler_new() {
117 let handler = InputHandler::new();
118 let _ = format!("{:?}", handler);
120 }
121
122 #[test]
123 fn test_input_handler_default() {
124 let handler = InputHandler;
125 let _ = format!("{:?}", handler);
126 }
127
128 #[test]
131 fn test_handle_digit_keys() {
132 let handler = InputHandler::new();
133 for c in '0'..='9' {
134 let event = key_event(KeyCode::Char(c));
135 assert_eq!(handler.handle_key(event), KeyAction::InsertChar(c));
136 }
137 }
138
139 #[test]
140 fn test_handle_operator_keys() {
141 let handler = InputHandler::new();
142 let operators = ['+', '-', '*', '/', '%', '^'];
143 for c in operators {
144 let event = key_event(KeyCode::Char(c));
145 assert_eq!(handler.handle_key(event), KeyAction::InsertChar(c));
146 }
147 }
148
149 #[test]
150 fn test_handle_parentheses() {
151 let handler = InputHandler::new();
152 assert_eq!(
153 handler.handle_key(key_event(KeyCode::Char('('))),
154 KeyAction::InsertChar('(')
155 );
156 assert_eq!(
157 handler.handle_key(key_event(KeyCode::Char(')'))),
158 KeyAction::InsertChar(')')
159 );
160 }
161
162 #[test]
163 fn test_handle_decimal_point() {
164 let handler = InputHandler::new();
165 assert_eq!(
166 handler.handle_key(key_event(KeyCode::Char('.'))),
167 KeyAction::InsertChar('.')
168 );
169 }
170
171 #[test]
172 fn test_handle_space() {
173 let handler = InputHandler::new();
174 assert_eq!(
175 handler.handle_key(key_event(KeyCode::Char(' '))),
176 KeyAction::InsertChar(' ')
177 );
178 }
179
180 #[test]
183 fn test_handle_backspace() {
184 let handler = InputHandler::new();
185 assert_eq!(
186 handler.handle_key(key_event(KeyCode::Backspace)),
187 KeyAction::Backspace
188 );
189 }
190
191 #[test]
192 fn test_handle_delete() {
193 let handler = InputHandler::new();
194 assert_eq!(
195 handler.handle_key(key_event(KeyCode::Delete)),
196 KeyAction::Delete
197 );
198 }
199
200 #[test]
203 fn test_handle_left() {
204 let handler = InputHandler::new();
205 assert_eq!(
206 handler.handle_key(key_event(KeyCode::Left)),
207 KeyAction::CursorLeft
208 );
209 }
210
211 #[test]
212 fn test_handle_right() {
213 let handler = InputHandler::new();
214 assert_eq!(
215 handler.handle_key(key_event(KeyCode::Right)),
216 KeyAction::CursorRight
217 );
218 }
219
220 #[test]
221 fn test_handle_home() {
222 let handler = InputHandler::new();
223 assert_eq!(
224 handler.handle_key(key_event(KeyCode::Home)),
225 KeyAction::CursorHome
226 );
227 }
228
229 #[test]
230 fn test_handle_end() {
231 let handler = InputHandler::new();
232 assert_eq!(
233 handler.handle_key(key_event(KeyCode::End)),
234 KeyAction::CursorEnd
235 );
236 }
237
238 #[test]
241 fn test_handle_enter() {
242 let handler = InputHandler::new();
243 assert_eq!(
244 handler.handle_key(key_event(KeyCode::Enter)),
245 KeyAction::Evaluate
246 );
247 }
248
249 #[test]
250 fn test_handle_escape() {
251 let handler = InputHandler::new();
252 assert_eq!(
253 handler.handle_key(key_event(KeyCode::Esc)),
254 KeyAction::Clear
255 );
256 }
257
258 #[test]
259 fn test_handle_up() {
260 let handler = InputHandler::new();
261 assert_eq!(
262 handler.handle_key(key_event(KeyCode::Up)),
263 KeyAction::RecallLast
264 );
265 }
266
267 #[test]
270 fn test_handle_ctrl_c() {
271 let handler = InputHandler::new();
272 assert_eq!(
273 handler.handle_key(key_event_ctrl(KeyCode::Char('c'))),
274 KeyAction::Quit
275 );
276 }
277
278 #[test]
279 fn test_handle_ctrl_q() {
280 let handler = InputHandler::new();
281 assert_eq!(
282 handler.handle_key(key_event_ctrl(KeyCode::Char('q'))),
283 KeyAction::Quit
284 );
285 }
286
287 #[test]
288 fn test_handle_ctrl_l() {
289 let handler = InputHandler::new();
290 assert_eq!(
291 handler.handle_key(key_event_ctrl(KeyCode::Char('l'))),
292 KeyAction::ClearAll
293 );
294 }
295
296 #[test]
297 fn test_handle_ctrl_a() {
298 let handler = InputHandler::new();
299 assert_eq!(
300 handler.handle_key(key_event_ctrl(KeyCode::Char('a'))),
301 KeyAction::CursorHome
302 );
303 }
304
305 #[test]
306 fn test_handle_ctrl_e() {
307 let handler = InputHandler::new();
308 assert_eq!(
309 handler.handle_key(key_event_ctrl(KeyCode::Char('e'))),
310 KeyAction::CursorEnd
311 );
312 }
313
314 #[test]
315 fn test_handle_ctrl_u() {
316 let handler = InputHandler::new();
317 assert_eq!(
318 handler.handle_key(key_event_ctrl(KeyCode::Char('u'))),
319 KeyAction::Clear
320 );
321 }
322
323 #[test]
324 fn test_handle_ctrl_unknown() {
325 let handler = InputHandler::new();
326 assert_eq!(
327 handler.handle_key(key_event_ctrl(KeyCode::Char('x'))),
328 KeyAction::None
329 );
330 }
331
332 #[test]
335 fn test_handle_unknown_key() {
336 let handler = InputHandler::new();
337 assert_eq!(
338 handler.handle_key(key_event(KeyCode::F(1))),
339 KeyAction::None
340 );
341 }
342
343 #[test]
344 fn test_handle_tab() {
345 let handler = InputHandler::new();
346 assert_eq!(handler.handle_key(key_event(KeyCode::Tab)), KeyAction::None);
347 }
348
349 #[test]
352 fn test_is_valid_char_digits() {
353 for c in '0'..='9' {
354 assert!(InputHandler::is_valid_char(c), "Digit {c} should be valid");
355 }
356 }
357
358 #[test]
359 fn test_is_valid_char_operators() {
360 let valid = ['+', '-', '*', '/', '%', '^', '(', ')', '.', ' '];
361 for c in valid {
362 assert!(InputHandler::is_valid_char(c), "Char '{c}' should be valid");
363 }
364 }
365
366 #[test]
367 fn test_is_valid_char_invalid() {
368 let invalid = ['a', 'z', 'A', 'Z', '@', '#', '$', '!', '&', '|'];
369 for c in invalid {
370 assert!(
371 !InputHandler::is_valid_char(c),
372 "Char '{c}' should be invalid"
373 );
374 }
375 }
376
377 #[test]
380 fn test_key_action_debug() {
381 let action = KeyAction::Evaluate;
382 assert!(format!("{:?}", action).contains("Evaluate"));
383 }
384
385 #[test]
386 fn test_key_action_clone() {
387 let action = KeyAction::InsertChar('x');
388 let cloned = action;
389 assert_eq!(action, cloned);
390 }
391
392 #[test]
393 fn test_key_action_copy() {
394 let action = KeyAction::Quit;
395 let copied: KeyAction = action;
396 assert_eq!(action, copied);
397 }
398}