smart_read/
basics.rs

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
26/// Takes an input that isn't empty
27pub 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
54/// Takes an input that contains non-whitespace chars
55pub 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
82/// Allows you to take a bool input
83pub 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
113/// Allows you to take a bool input
114pub 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
178/// Allows you take take a char input
179pub struct CharInput;
180implement_number_input!(CharInput, char, "Enter a character: ");
181
182/// Allows you take take a u8 input
183pub struct U8Input;
184implement_number_input!(U8Input, u8, "Enter a number (positive integer): ");
185
186/// Allows you take take an i8 input
187pub struct I8Input;
188implement_number_input!(I8Input, i8, "Enter a number (integer): ");
189
190/// Allows you take take a u16 input
191pub struct U16Input;
192implement_number_input!(U16Input, u16, "Enter a number (positive integer): ");
193
194/// Allows you take take an i16 input
195pub struct I16Input;
196implement_number_input!(I16Input, i16, "Enter a number (integer): ");
197
198/// Allows you take take a u32 input
199pub struct U32Input;
200implement_number_input!(U32Input, u32, "Enter a number (positive integer): ");
201
202/// Allows you take take an i32 input
203pub struct I32Input;
204implement_number_input!(I32Input, i32, "Enter a number (integer): ");
205
206/// Allows you take take a u64 input
207pub struct U64Input;
208implement_number_input!(U64Input, u64, "Enter a number (positive integer): ");
209
210/// Allows you take take an i64 input
211pub struct I64Input;
212implement_number_input!(I64Input, i64, "Enter a number (integer): ");
213
214/// Allows you take take a u128 input
215pub struct U128Input;
216implement_number_input!(U128Input, u128, "Enter a number (positive integer): ");
217
218/// Allows you take take an i128 input
219pub struct I128Input;
220implement_number_input!(I128Input, i128, "Enter a number (integer): ");
221
222/// Allows you take take a usize input
223pub struct UsizeInput;
224implement_number_input!(UsizeInput, usize, "Enter a number (positive integer): ");
225
226/// Allows you take take an isize input
227pub struct IsizeInput;
228implement_number_input!(IsizeInput, isize, "Enter a number (integer): ");
229
230/// Allows you take take an f32 input
231pub struct F32Input;
232implement_number_input!(F32Input, f32, "Enter a number: ");
233
234/// Allows you take take an f64 input
235pub struct F64Input;
236implement_number_input!(F64Input, f64, "Enter a number: ");