1use std::str::FromStr;
2
3use thiserror::Error;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
6pub struct Interval {
7 pub min: usize,
8 pub max: usize,
9}
10
11impl Interval {
12 pub fn new(min: usize, max: usize) -> Option<Self> {
13 if min <= max {
14 Some(Self { min, max })
15 } else {
16 None
17 }
18 }
19
20 pub fn at_least(size: usize) -> Self {
21 Self {
22 min: size,
23 max: usize::MAX,
24 }
25 }
26
27 pub fn at_most(size: usize) -> Self {
28 Self {
29 min: usize::MIN,
30 max: size,
31 }
32 }
33
34 pub fn exactly(size: usize) -> Self {
35 Self {
36 min: size,
37 max: size,
38 }
39 }
40
41 pub fn safe(a: usize, b: usize) -> Self {
42 if a <= b {
43 Self { min: a, max: b }
44 } else {
45 Self { min: b, max: a }
46 }
47 }
48}
49
50#[derive(Debug, Error)]
51pub enum IntervalParseError {
52 #[error("Expect the interval to have the first value <= the second, got {0} <= {1}")]
53 BadBounds(usize, usize),
54 #[error("got `{0}`, expect the format for an interval to be: N, N+, N-, or A-B")]
55 ImproperFormat(String),
56}
57
58impl FromStr for Interval {
59 type Err = IntervalParseError;
60 fn from_str(s: &str) -> Result<Self, Self::Err> {
61 enum Style {
62 Exact,
63 AtLeast,
64 AtMost,
65 Range,
66 }
67 let s = s.trim();
68 let mut first = vec![];
69 let mut second = vec![];
70 let mut style = Style::Exact;
71 for (i, c) in s.chars().enumerate() {
72 if matches!(style, Style::Exact) {
73 if c.is_numeric() {
74 first.push(c);
75 } else if c == '+' && i + 1 == s.len() {
76 style = Style::AtLeast;
77 } else if c == '-' && i + 1 == s.len() {
78 style = Style::AtMost;
79 } else if c == '-' {
80 style = Style::Range;
81 } else {
82 return Err(IntervalParseError::ImproperFormat(s.to_string()));
83 }
84 } else if matches!(style, Style::Range) {
85 if c.is_numeric() {
86 second.push(c);
87 } else {
88 return Err(IntervalParseError::ImproperFormat(s.to_string()));
89 }
90 }
91 }
92 match style {
93 Style::Exact => {
94 let n = first
95 .into_iter()
96 .collect::<String>()
97 .parse()
98 .map_err(|_| IntervalParseError::ImproperFormat(s.to_string()))?;
99 Ok(Interval { min: n, max: n })
100 }
101 Style::AtLeast => {
102 let n = first
103 .into_iter()
104 .collect::<String>()
105 .parse()
106 .map_err(|_| IntervalParseError::ImproperFormat(s.to_string()))?;
107 Ok(Interval {
108 min: n,
109 max: usize::MAX,
110 })
111 }
112 Style::AtMost => {
113 let n = first
114 .into_iter()
115 .collect::<String>()
116 .parse()
117 .map_err(|_| IntervalParseError::ImproperFormat(s.to_string()))?;
118 Ok(Interval {
119 min: usize::MIN,
120 max: n,
121 })
122 }
123 Style::Range => {
124 let a = first
125 .into_iter()
126 .collect::<String>()
127 .parse()
128 .map_err(|_| IntervalParseError::ImproperFormat(s.to_string()))?;
129 let b = second
130 .into_iter()
131 .collect::<String>()
132 .parse()
133 .map_err(|_| IntervalParseError::ImproperFormat(s.to_string()))?;
134 if a <= b {
135 Ok(Interval { min: a, max: b })
136 } else {
137 Err(IntervalParseError::BadBounds(a, b))
138 }
139 }
140 }
141 }
142}