smart_read/
range_constraints.rs

1use crate::*;
2use std::{ops::{Range, RangeBounds, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive}, str::FromStr};
3
4
5
6/// Internal utility function
7pub 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}