1use crate::*;
2
3
4
5impl TryRead for () {
6 type Output = String;
7 type Default = String;
8 fn try_read_line(self, prompt: Option<String>, default: Option<Self::Default>) -> BoxResult<Self::Output> {
9 match (prompt, &default) {
10 (Some(prompt), Some(default)) => print!("{prompt}(default: {default}) "),
11 (None, Some(default)) => print!("(default: {default}) "),
12 (Some(prompt), None) => print!("{prompt}"),
13 (None, None) => {},
14 }
15 let output = read_stdin()?;
16 Ok(if output.is_empty() && let Some(default) = default {
17 default.to_string()
18 } else {
19 output
20 })
21 }
22}
23
24
25
26pub struct NonEmptyInput;
28
29impl TryRead for NonEmptyInput {
30 type Output = String;
31 type Default = String;
32 fn try_read_line(self, prompt: Option<String>, default: Option<Self::Default>) -> BoxResult<Self::Output> {
33 let mut prompt = prompt.unwrap_or_default();
34 if let Some(default) = default.as_ref() {
35 prompt += &format!("(default: {default}) ");
36 }
37 loop {
38
39 print!("{prompt}");
40 let input = read_stdin()?;
41 if input.is_empty() {
42 println!();
43 println!("Invalid input, must not be empty");
44 continue;
45 }
46 return BoxResult::Ok(input);
47
48 }
49 }
50}
51
52
53
54pub struct NonWhitespaceInput;
56
57impl TryRead for NonWhitespaceInput {
58 type Output = String;
59 type Default = String;
60 fn try_read_line(self, prompt: Option<String>, default: Option<Self::Default>) -> BoxResult<Self::Output> {
61 let mut prompt = prompt.unwrap_or_default();
62 if let Some(default) = default.as_ref() {
63 prompt += &format!("(default: {default}) ");
64 }
65 loop {
66
67 print!("{prompt}");
68 let input = read_stdin()?;
69 if input.trim().is_empty() {
70 println!();
71 println!("Invalid input, must contain non-whitespace characters");
72 continue;
73 }
74 return BoxResult::Ok(input);
75
76 }
77 }
78}
79
80
81
82pub struct BoolInput;
84
85impl TryRead for BoolInput {
86 type Output = bool;
87 type Default = bool;
88 fn try_read_line(self, prompt: Option<String>, default: Option<Self::Default>) -> BoxResult<Self::Output> {
89 let mut prompt = prompt.unwrap_or(String::from("Enter a bool: "));
90 if let Some(default) = default.as_ref() {
91 prompt += &format!("(default: {default}) ");
92 }
93 loop {
94
95 print!("{prompt}");
96 let input = read_stdin()?.to_lowercase();
97 match (&*input, default) {
98 ("", Some(default)) => return Ok(default),
99 ("true", _) | ("t", _) => return Ok(true),
100 ("false", _) | ("f", _) => return Ok(false),
101 (_, _) => {
102 println!();
103 println!("Invalid input, please enter \"true\" or \"false\"");
104 }
105 }
106
107 }
108 }
109}
110
111
112
113pub struct YesNoInput;
115
116impl TryRead for YesNoInput {
117 type Output = bool;
118 type Default = bool;
119 fn try_read_line(self, prompt: Option<String>, default: Option<Self::Default>) -> BoxResult<Self::Output> {
120 let mut prompt = prompt.unwrap_or(String::from("Enter 'Yes' or 'No': "));
121 if let Some(default) = default.as_ref() {
122 prompt += &format!("(default: {}) ", if *default {"Yes"} else {"No"});
123 }
124 loop {
125
126 print!("{prompt}");
127 let input = read_stdin()?.to_lowercase();
128 match (&*input, default) {
129 ("", Some(default)) => return Ok(default),
130 ("yes", _) | ("y", _) => return Ok(true),
131 ("no", _) | ("n", _) => return Ok(false),
132 (_, _) => {
133 println!();
134 println!("Invalid input, please enter \"yes\" or \"no\"");
135 }
136 }
137
138 }
139 }
140}
141
142
143
144macro_rules! implement_number_input {
145 ($type_name:tt, $type_base:ty, $default_prompt:expr) => {
146 impl TryRead for $type_name {
147 type Output = $type_base;
148 type Default = $type_base;
149 fn try_read_line(self, prompt: Option<String>, default: Option<Self::Default>) -> BoxResult<Self::Output> {
150 let mut prompt = prompt.unwrap_or(String::from($default_prompt));
151 if let Some(default) = default.as_ref() {
152 prompt += &format!("(default: {default}) ");
153 }
154 loop {
155
156 print!("{prompt}");
157 let input_string = read_stdin()?;
158 if input_string.is_empty() && let Some(default) = default {
159 return Ok(default);
160 }
161
162 let input = match input_string.parse::<$type_base>() {
163 Ok(v) => v,
164 Err(err) => {
165 println!();
166 println!("Could not parse input (error: {err})");
167 continue;
168 }
169 };
170 return Ok(input);
171
172 }
173 }
174 }
175 };
176}
177
178pub struct CharInput;
180implement_number_input!(CharInput, char, "Enter a character: ");
181
182pub struct U8Input;
184implement_number_input!(U8Input, u8, "Enter a number (positive integer): ");
185
186pub struct I8Input;
188implement_number_input!(I8Input, i8, "Enter a number (integer): ");
189
190pub struct U16Input;
192implement_number_input!(U16Input, u16, "Enter a number (positive integer): ");
193
194pub struct I16Input;
196implement_number_input!(I16Input, i16, "Enter a number (integer): ");
197
198pub struct U32Input;
200implement_number_input!(U32Input, u32, "Enter a number (positive integer): ");
201
202pub struct I32Input;
204implement_number_input!(I32Input, i32, "Enter a number (integer): ");
205
206pub struct U64Input;
208implement_number_input!(U64Input, u64, "Enter a number (positive integer): ");
209
210pub struct I64Input;
212implement_number_input!(I64Input, i64, "Enter a number (integer): ");
213
214pub struct U128Input;
216implement_number_input!(U128Input, u128, "Enter a number (positive integer): ");
217
218pub struct I128Input;
220implement_number_input!(I128Input, i128, "Enter a number (integer): ");
221
222pub struct UsizeInput;
224implement_number_input!(UsizeInput, usize, "Enter a number (positive integer): ");
225
226pub struct IsizeInput;
228implement_number_input!(IsizeInput, isize, "Enter a number (integer): ");
229
230pub struct F32Input;
232implement_number_input!(F32Input, f32, "Enter a number: ");
233
234pub struct F64Input;
236implement_number_input!(F64Input, f64, "Enter a number: ");