smart_read/
range_constraints.rs1use crate::*;
2use std::{ops::{Range, RangeBounds, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive}, str::FromStr};
3
4
5
6pub fn read_range<T, R>(range: R, mut prompt: String, default: Option<T>) -> BoxResult<T>
8where
9 T: Display + FromStr + PartialOrd<T>,
10 R: RangeBounds<T>,
11 <T as FromStr>::Err: Display,
12{
13 if let Some(default) = default.as_ref() {
14 prompt += &format!("(default: {default}) ");
15 }
16 loop {
17
18 print!("{prompt}");
19 let output_string = read_stdin()?;
20 if output_string.is_empty() && let Some(default) = default {
21 return Ok(default);
22 }
23
24 let output = match output_string.parse::<T>() {
25 Ok(v) => v,
26 Err(err) => {
27 println!();
28 println!("Could not parse input ({err})");
29 continue;
30 }
31 };
32 if range.contains(&output) {
33 return Ok(output);
34 }
35
36 println!();
37 println!("Invalid input, not within bounds");
38 }
39}
40
41
42
43impl<T> TryRead for Range<T>
44where
45 T: Display + FromStr + PartialOrd<T>,
46 <T as FromStr>::Err: Display,
47{
48 type Output = T;
49 type Default = T;
50 fn try_read_line(self, prompt: Option<String>, default: Option<Self::Output>) -> BoxResult<Self::Output> {
51 let prompt = prompt.unwrap_or_else(|| format!("Enter a number within the range [{:.1}, {:.1}): ", self.start, self.end));
52 read_range(self, prompt, default)
53 }
54}
55
56impl<T> TryRead for RangeInclusive<T>
57where
58 T: Display + FromStr + PartialOrd<T>,
59 <T as FromStr>::Err: Display,
60{
61 type Output = T;
62 type Default = T;
63 fn try_read_line(self, prompt: Option<String>, default: Option<Self::Output>) -> BoxResult<Self::Output> {
64 let prompt = prompt.unwrap_or_else(|| format!("Enter a number within the range [{:.1}, {:.1}]: ", self.start(), self.end()));
65 read_range(self, prompt, default)
66 }
67}
68
69impl<T> TryRead for RangeTo<T>
70where
71 T: Display + FromStr + PartialOrd<T>,
72 <T as FromStr>::Err: Display,
73{
74 type Output = T;
75 type Default = T;
76 fn try_read_line(self, prompt: Option<String>, default: Option<Self::Output>) -> BoxResult<Self::Output> {
77 let prompt = prompt.unwrap_or_else(|| format!("Enter a number which is less than {:.1}: ", self.end));
78 read_range(self, prompt, default)
79 }
80}
81
82impl<T> TryRead for RangeFrom<T>
83where
84 T: Display + FromStr + PartialOrd<T>,
85 <T as FromStr>::Err: Display,
86{
87 type Output = T;
88 type Default = T;
89 fn try_read_line(self, prompt: Option<String>, default: Option<Self::Output>) -> BoxResult<Self::Output> {
90 let prompt = prompt.unwrap_or_else(|| format!("Enter a number which is at least {:.1}", self.start));
91 read_range(self, prompt, default)
92 }
93}
94
95impl<T> TryRead for RangeToInclusive<T>
96where
97 T: Display + FromStr + PartialOrd<T>,
98 <T as FromStr>::Err: Display,
99{
100 type Output = T;
101 type Default = T;
102 fn try_read_line(self, prompt: Option<String>, default: Option<Self::Output>) -> BoxResult<Self::Output> {
103 let prompt = prompt.unwrap_or_else(|| format!("Enter a number which is at most {:.1}: ", self.end));
104 read_range(self, prompt, default)
105 }
106}