archetect_core/vendor/read_input/
mod.rs1#![deny(clippy::pedantic, missing_docs)]
4#![allow(clippy::must_use_candidate)]
5#![allow(clippy::needless_pass_by_value)]
7
8use std::{cmp::PartialOrd, io, rc::Rc, str::FromStr, string::ToString};
9use std::cell::RefCell;
10use std::io::Write;
11
12use crate::vendor::read_input::core::read_input;
13use crate::vendor::read_input::test_generators::InsideFunc;
14
15mod core;
16pub mod prelude;
17pub mod shortcut;
18mod test_generators;
19#[cfg(test)]
20mod tests;
21
22const DEFAULT_ERR: &str = "That value does not pass. Please try again";
23
24pub trait InputBuild<T: FromStr> {
26 fn msg(self, msg: impl ToString) -> Self;
28 fn repeat_msg(self, msg: impl ToString) -> Self;
30 fn err(self, err: impl ToString) -> Self;
32 fn add_test<F: Fn(&T) -> bool + 'static>(self, test: F) -> Self;
34 fn add_err_test<F>(self, test: F, err: impl ToString) -> Self
37 where
38 F: Fn(&T) -> bool + 'static;
39 fn clear_tests(self) -> Self;
42 fn err_match<F>(self, err_match: F) -> Self
44 where
45 F: Fn(&T::Err) -> Option<String> + 'static;
46 fn inside<U: InsideFunc<T>>(self, constraint: U) -> Self;
48 fn inside_err<U: InsideFunc<T>>(self, constraint: U, err: impl ToString) -> Self;
51 fn toggle_msg_repeat(self) -> Self;
53 fn prompting_on(self, prompt_output: RefCell<Box<dyn Write>>) -> Self;
55 fn prompting_on_stderr(self) -> Self;
57}
58
59pub trait InputConstraints<T>: InputBuild<T>
62where
63 T: FromStr + PartialOrd + 'static,
64 Self: Sized,
65{
66 fn min(self, min: T) -> Self {
68 self.inside(min..)
69 }
70 fn min_err(self, min: T, err: impl ToString) -> Self {
72 self.inside_err(min.., err)
73 }
74 fn max(self, max: T) -> Self {
76 self.inside(..=max)
77 }
78 fn max_err(self, max: T, err: impl ToString) -> Self {
80 self.inside_err(..=max, err)
81 }
82 fn min_max(self, min: T, max: T) -> Self {
84 self.inside(min..=max)
85 }
86 fn min_max_err(self, min: T, max: T, err: impl ToString) -> Self {
88 self.inside_err(min..=max, err)
89 }
90 fn not(self, this: T) -> Self {
92 self.add_test(move |x: &T| *x != this)
93 }
94 fn not_err(self, this: T, err: impl ToString) -> Self {
96 self.add_err_test(move |x: &T| *x != this, err)
97 }
98}
99
100#[derive(Clone)]
101pub(crate) struct Prompt {
102 pub msg: String,
103 pub repeat: bool,
104}
105
106#[derive(Clone)]
107pub(crate) struct Test<T> {
108 pub func: Rc<dyn Fn(&T) -> bool>,
109 pub err: Option<String>,
110}
111
112pub struct InputBuilder<T: FromStr> {
118 msg: Prompt,
119 err: String,
120 tests: Vec<Test<T>>,
121 err_match: Rc<dyn Fn(&T::Err) -> Option<String>>,
122 prompt_output: RefCell<Box<dyn Write>>,
123}
124
125impl<T: FromStr> InputBuilder<T> {
126 pub fn new() -> Self {
128 Self {
129 msg: Prompt {
130 msg: String::new(),
131 repeat: false,
132 },
133 err: DEFAULT_ERR.to_string(),
134 tests: Vec::new(),
135 err_match: Rc::new(|_| None),
136 prompt_output: RefCell::new(Box::new(std::io::stdout())),
137 }
138 }
139 pub fn get(&self) -> T {
143 self.try_get().expect("Failed to read line")
144 }
145 pub fn try_get(&self) -> io::Result<T> {
151 read_input::<T>(
152 &self.msg,
153 &self.err,
154 None,
155 &self.tests,
156 &*self.err_match,
157 &mut (*self.prompt_output.borrow_mut()),
158 )
159 }
160 pub fn default(self, default: T) -> InputBuilderOnce<T> {
162 InputBuilderOnce {
163 builder: self,
164 default: Some(default),
165 }
166 }
167 fn test_err_opt(mut self, func: Rc<dyn Fn(&T) -> bool>, err: Option<String>) -> Self {
169 self.tests.push(Test { func, err });
170 self
171 }
172}
173
174impl<T: FromStr> InputBuild<T> for InputBuilder<T> {
175 fn msg(mut self, msg: impl ToString) -> Self {
176 self.msg = Prompt {
177 msg: msg.to_string(),
178 repeat: false,
179 };
180 self
181 }
182 fn repeat_msg(mut self, msg: impl ToString) -> Self {
183 self.msg = Prompt {
184 msg: msg.to_string(),
185 repeat: true,
186 };
187 self
188 }
189 fn err(mut self, err: impl ToString) -> Self {
190 self.err = err.to_string();
191 self
192 }
193
194 fn add_test<F: Fn(&T) -> bool + 'static>(self, test: F) -> Self {
195 self.test_err_opt(Rc::new(test), None)
196 }
197 fn add_err_test<F>(self, test: F, err: impl ToString) -> Self
198 where
199 F: Fn(&T) -> bool + 'static,
200 {
201 self.test_err_opt(Rc::new(test), Some(err.to_string()))
202 }
203 fn clear_tests(mut self) -> Self {
204 self.tests = Vec::new();
205 self
206 }
207 fn err_match<F>(mut self, err_match: F) -> Self
208 where
209 F: Fn(&T::Err) -> Option<String> + 'static,
210 {
211 self.err_match = Rc::new(err_match);
212 self
213 }
214 fn inside<U: InsideFunc<T>>(self, constraint: U) -> Self {
215 self.test_err_opt(constraint.contains_func(), None)
216 }
217 fn inside_err<U: InsideFunc<T>>(self, constraint: U, err: impl ToString) -> Self {
218 self.test_err_opt(constraint.contains_func(), Some(err.to_string()))
219 }
220 fn toggle_msg_repeat(mut self) -> Self {
221 self.msg.repeat = !self.msg.repeat;
222 self
223 }
224
225 fn prompting_on(mut self, prompt_output: RefCell<Box<dyn Write>>) -> Self {
226 self.prompt_output = prompt_output;
227 self
228 }
229
230 fn prompting_on_stderr(self) -> Self {
231 self.prompting_on(RefCell::new(Box::new(std::io::stderr())))
232 }
233}
234
235impl<T: FromStr + PartialOrd + 'static> InputConstraints<T> for InputBuilder<T> {}
236
237impl<T: FromStr> Default for InputBuilder<T> {
238 fn default() -> Self {
239 Self::new()
240 }
241}
242
243impl<T: FromStr + Clone> Clone for InputBuilder<T> {
244 fn clone(&self) -> Self {
245 Self {
246 msg: self.msg.clone(),
247 err: self.err.clone(),
248 tests: self.tests.clone(),
249 err_match: self.err_match.clone(),
250 prompt_output: RefCell::new(Box::new(std::io::stdout())),
251 }
252 }
253}
254
255pub struct InputBuilderOnce<T: FromStr> {
261 builder: InputBuilder<T>,
262 default: Option<T>,
263}
264
265impl<T: FromStr> InputBuilderOnce<T> {
266 pub fn get(self) -> T {
270 self.try_get().expect("Failed to read line")
271 }
272 pub fn try_get(self) -> io::Result<T> {
278 read_input::<T>(
279 &self.builder.msg,
280 &self.builder.err,
281 self.default,
282 &self.builder.tests,
283 &*self.builder.err_match,
284 &mut (*self.builder.prompt_output.borrow_mut()),
285 )
286 }
287 fn internal<F>(self, with: F) -> Self
289 where
290 F: FnOnce(InputBuilder<T>) -> InputBuilder<T>,
291 {
292 Self {
293 builder: with(self.builder),
294 ..self
295 }
296 }
297}
298
299impl<T: FromStr> InputBuild<T> for InputBuilderOnce<T> {
300 fn msg(self, msg: impl ToString) -> Self {
301 self.internal(|x| x.msg(msg))
302 }
303 fn repeat_msg(self, msg: impl ToString) -> Self {
304 self.internal(|x| x.repeat_msg(msg))
305 }
306 fn err(self, err: impl ToString) -> Self {
307 self.internal(|x| x.err(err))
308 }
309 fn add_test<F: Fn(&T) -> bool + 'static>(self, test: F) -> Self {
310 self.internal(|x| x.add_test(test))
311 }
312 fn add_err_test<F>(self, test: F, err: impl ToString) -> Self
313 where
314 F: Fn(&T) -> bool + 'static,
315 {
316 self.internal(|x| x.add_err_test(test, err))
317 }
318 fn clear_tests(self) -> Self {
319 self.internal(InputBuild::clear_tests)
320 }
321 fn err_match<F>(self, err_match: F) -> Self
322 where
323 F: Fn(&T::Err) -> Option<String> + 'static,
324 {
325 self.internal(|x| x.err_match(err_match))
326 }
327 fn inside<U: InsideFunc<T>>(self, constraint: U) -> Self {
328 self.internal(|x| x.inside(constraint))
329 }
330 fn inside_err<U: InsideFunc<T>>(self, constraint: U, err: impl ToString) -> Self {
331 self.internal(|x| x.inside_err(constraint, err))
332 }
333 fn toggle_msg_repeat(self) -> Self {
334 self.internal(InputBuild::toggle_msg_repeat)
335 }
336
337 fn prompting_on(self, prompt_output: RefCell<Box<dyn Write>>) -> Self {
338 self.internal(|x| x.prompting_on(prompt_output))
339 }
340
341 fn prompting_on_stderr(self) -> Self {
342 self.internal(|x| x.prompting_on(RefCell::new(Box::new(std::io::stderr()))))
343 }
344}
345
346impl<T: FromStr + PartialOrd + 'static> InputConstraints<T> for InputBuilderOnce<T> {}
347
348impl<T> Clone for InputBuilderOnce<T>
349where
350 T: Clone + FromStr,
351{
352 fn clone(&self) -> Self {
353 Self {
354 default: self.default.clone(),
355 builder: self.builder.clone(),
356 }
357 }
358}