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