1use crate::Other;
2
3pub enum NumberConstraint {
4 None,
5 Positive,
6 NonNegative,
7 Range(f64, f64),
8}
9
10pub enum BoolType {
11 TrueFalse,
12 YesNo,
13}
14
15#[derive(Debug, PartialEq, Eq)]
17pub enum Bool {
18 Ok(bool),
19 Other(Other),
20}
21
22impl Default for Bool {
23 fn default() -> Self {
24 Bool::Ok(false)
25 }
26}
27
28impl Bool {
29 pub fn parse(s: &str, bool_type: BoolType) -> Self {
30 match bool_type {
31 BoolType::TrueFalse => match s {
32 "true" => Bool::Ok(true),
33 "false" => Bool::Ok(false),
34 _ => Bool::Other((s.to_string(), "should be \"true\" or \"false\"".to_string())),
35 },
36 BoolType::YesNo => match s {
37 "yes" => Bool::Ok(true),
38 "no" => Bool::Ok(false),
39 _ => Bool::Other((s.to_string(), "should be \"yes\" or \"no\"".to_string())),
40 },
41 }
42 }
43}
44
45impl std::str::FromStr for Bool {
46 type Err = String;
47
48 fn from_str(s: &str) -> Result<Self, Self::Err> {
49 match s.parse::<bool>() {
50 Ok(x) => Ok(Self::Ok(x)),
51 Err(e) => Ok(Self::Other((s.to_string(), e.to_string()))),
52 }
53 }
54}
55
56impl std::fmt::Display for Bool {
57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 match self {
59 Self::Ok(t) => write!(f, "{t}"),
60 Self::Other((s, _)) => write!(f, "{s}"),
61 }
62 }
63}
64
65#[derive(Debug, PartialEq, Eq)]
67pub enum Integer {
68 Ok(i64),
69 Other(Other),
70}
71
72impl Integer {
73 pub fn parse(s: &str, constraint: NumberConstraint) -> Self {
74 match s.parse::<i64>() {
75 Ok(x) => match constraint {
76 NumberConstraint::None => Self::Ok(x),
77 NumberConstraint::Positive => {
78 if x > 0 {
79 Self::Ok(x)
80 } else {
81 Self::Other((s.to_string(), "should be positive".to_string()))
82 }
83 }
84 NumberConstraint::NonNegative => {
85 if x >= 0 {
86 Self::Ok(x)
87 } else {
88 Self::Other((s.to_string(), "should be non-negative".to_string()))
89 }
90 }
91 NumberConstraint::Range(min, max) => {
92 if x as f64 >= min && x as f64 <= max {
93 Self::Ok(x)
94 } else {
95 Self::Other((s.to_string(), format!("should be in range [{min}, {max}]")))
96 }
97 }
98 },
99 Err(_) => Self::Other((s.to_string(), "should be an integer".to_string())),
100 }
101 }
102}
103
104impl std::fmt::Display for Integer {
105 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106 match self {
107 Self::Ok(t) => write!(f, "{t}"),
108 Self::Other((s, _)) => write!(f, "{s}"),
109 }
110 }
111}
112
113#[derive(Debug, PartialEq)]
115pub enum Float {
116 Ok(f64),
117 Other(Other),
118}
119
120impl Float {
121 pub fn parse(s: &str, constraint: NumberConstraint) -> Self {
122 match s.parse::<f64>() {
123 Ok(x) => match constraint {
124 NumberConstraint::None => Self::Ok(x),
125 NumberConstraint::Positive => {
126 if x > 0.0 {
127 Self::Ok(x)
128 } else {
129 Self::Other((s.to_string(), "should be positive".to_string()))
130 }
131 }
132 NumberConstraint::NonNegative => {
133 if x >= 0.0 {
134 Self::Ok(x)
135 } else {
136 Self::Other((s.to_string(), "should be non-negative".to_string()))
137 }
138 }
139 NumberConstraint::Range(min, max) => {
140 if x >= min && x <= max {
141 Self::Ok(x)
142 } else {
143 Self::Other((s.to_string(), format!("should be in range [{min}, {max}]")))
144 }
145 }
146 },
147 Err(_) => Self::Other((
148 s.to_string(),
149 "should be a floating-point number".to_string(),
150 )),
151 }
152 }
153}
154
155impl std::fmt::Display for Float {
156 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
157 match self {
158 Self::Ok(t) => write!(f, "{t}"),
159 Self::Other((s, _)) => write!(f, "{s}"),
160 }
161 }
162}
163
164#[derive(Debug, PartialEq)]
168pub enum Number {
169 Integer(i64),
170 Float(f64),
171 Other(Other),
172}
173
174impl Number {
175 pub fn parse(s: &str, constraint: NumberConstraint) -> Self {
176 match s.parse::<i64>() {
177 Ok(x) => match constraint {
178 NumberConstraint::None => Self::Integer(x),
179 NumberConstraint::Positive => {
180 if x > 0 {
181 Self::Integer(x)
182 } else {
183 Self::Other((s.to_string(), "should be positive".to_string()))
184 }
185 }
186 NumberConstraint::NonNegative => {
187 if x >= 0 {
188 Self::Integer(x)
189 } else {
190 Self::Other((s.to_string(), "should be non-negative".to_string()))
191 }
192 }
193 NumberConstraint::Range(min, max) => {
194 if x as f64 >= min && x as f64 <= max {
195 Self::Integer(x)
196 } else {
197 Self::Other((s.to_string(), format!("should be in range [{min}, {max}]")))
198 }
199 }
200 },
201 Err(_) => match s.parse::<f64>() {
202 Ok(x) => match constraint {
203 NumberConstraint::None => Self::Float(x),
204 NumberConstraint::Positive => {
205 if x > 0.0 {
206 Self::Float(x)
207 } else {
208 Self::Other((s.to_string(), "should be positive".to_string()))
209 }
210 }
211 NumberConstraint::NonNegative => {
212 if x >= 0.0 {
213 Self::Float(x)
214 } else {
215 Self::Other((s.to_string(), "should be non-negative".to_string()))
216 }
217 }
218 NumberConstraint::Range(min, max) => {
219 if x >= min && x <= max {
220 Self::Float(x)
221 } else {
222 Self::Other((
223 s.to_string(),
224 format!("should be in range [{min}, {max}]"),
225 ))
226 }
227 }
228 },
229 Err(_) => Self::Other((s.to_string(), "should be a number".to_string())),
230 },
231 }
232 }
233}
234
235impl std::fmt::Display for Number {
236 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
237 match self {
238 Self::Integer(t) => write!(f, "{t}"),
239 Self::Float(t) => write!(f, "{t}"),
240 Self::Other((s, _)) => write!(f, "{s}"),
241 }
242 }
243}
244
245#[derive(Debug, PartialEq, Eq)]
247pub enum Duration {
248 Duration(chrono::Duration),
249 Other(Other),
250}
251
252impl Duration {
253 pub fn parse_from_int_string(s: &str) -> Self {
254 match s.parse::<u64>() {
255 Ok(x) => Self::Duration(chrono::Duration::seconds(x as i64)),
256 Err(_) => Self::Other((
257 s.to_string(),
258 "should be a non-negative integer".to_string(),
259 )),
260 }
261 }
262
263 pub fn parse_from_float_string(s: &str) -> Self {
264 match s.parse::<f64>() {
265 Ok(x) => {
266 let x = (x * 1_000_000_000.0).round() as i64;
268 Self::Duration(chrono::Duration::nanoseconds(x))
269 }
270 Err(_) => Self::Other((
271 s.to_string(),
272 "should be a non-negative floating-point number".to_string(),
273 )),
274 }
275 }
276}