1use super::editor::{EditMode, Editor};
7
8#[derive(Debug, Clone)]
10pub struct AmountWidget {
11 editor: Editor,
12}
13
14impl AmountWidget {
15 #[must_use]
16 pub fn new(mode: EditMode) -> Self {
17 Self {
18 editor: Editor::new(mode),
19 }
20 }
21
22 #[must_use]
23 pub fn with_value(mode: EditMode, value: impl Into<String>) -> Self {
24 Self {
25 editor: Editor::with_buffer(mode, value),
26 }
27 }
28
29 #[must_use]
31 pub fn value(&self) -> &str {
32 self.editor.buffer()
33 }
34
35 pub fn insert_char(&mut self, c: char) {
37 if is_amount_char(c) {
38 self.editor.insert_char(c);
39 }
40 }
41
42 #[must_use]
44 pub fn as_editor_mut(&mut self) -> &mut Editor {
45 &mut self.editor
46 }
47}
48
49fn is_amount_char(c: char) -> bool {
50 c.is_ascii_digit() || matches!(c, '-' | '.' | '/')
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56
57 #[test]
58 fn accepts_digits_and_separators() {
59 let mut w = AmountWidget::new(EditMode::Emacs);
60 for c in "100.5/3".chars() {
61 w.insert_char(c);
62 }
63 assert_eq!(w.value(), "100.5/3");
64 }
65
66 #[test]
67 fn accepts_negative_sign() {
68 let mut w = AmountWidget::new(EditMode::Emacs);
69 w.insert_char('-');
70 w.insert_char('5');
71 assert_eq!(w.value(), "-5");
72 }
73
74 #[test]
75 fn rejects_letters() {
76 let mut w = AmountWidget::new(EditMode::Emacs);
77 w.insert_char('a');
78 w.insert_char('b');
79 assert_eq!(w.value(), "");
80 }
81
82 #[test]
83 fn rejects_space_and_punctuation() {
84 let mut w = AmountWidget::new(EditMode::Emacs);
85 w.insert_char(' ');
86 w.insert_char('@');
87 w.insert_char('#');
88 assert_eq!(w.value(), "");
89 }
90
91 #[test]
92 fn delete_backward_via_editor_mut() {
93 let mut w = AmountWidget::new(EditMode::Emacs);
94 w.insert_char('5');
95 w.insert_char('0');
96 w.as_editor_mut().delete_backward();
97 assert_eq!(w.value(), "5");
98 }
99
100 #[test]
101 fn with_value_sets_initial_buffer() {
102 let w = AmountWidget::with_value(EditMode::Emacs, "42/7");
103 assert_eq!(w.value(), "42/7");
104 }
105}