tanzim_validate/
duration.rs1use crate::error::{Error, ErrorKind};
2use crate::{Meta, Validator};
3use tanzim_value::{Value, ValueType};
4
5#[derive(Debug, Clone, Default)]
8pub struct Duration {
9 meta: Meta,
10 millis: bool,
11}
12
13impl Duration {
14 pub fn with_meta(mut self, meta: Meta) -> Self {
16 self.meta = meta;
17 self
18 }
19
20 pub fn new() -> Self {
21 Self::default()
22 }
23
24 pub fn millis(mut self) -> Self {
26 self.millis = true;
27 self
28 }
29}
30
31impl Validator for Duration {
32 fn meta(&self) -> &Meta {
33 &self.meta
34 }
35
36 fn meta_mut(&mut self) -> &mut Meta {
37 &mut self.meta
38 }
39
40 fn check(&self, value: &mut Value) -> Result<(), Error> {
41 let text = match value {
42 Value::String(text) => text,
43 other => {
44 return Err(Error::new(ErrorKind::Type {
45 expected: ValueType::String,
46 found: other.type_name(),
47 }));
48 }
49 };
50
51 let parsed = match humantime::parse_duration(text) {
52 Ok(parsed) => parsed,
53 Err(_) => {
54 return Err(Error::new(ErrorKind::Format {
55 expected: "duration",
56 }));
57 }
58 };
59
60 let amount = if self.millis {
61 parsed.as_millis()
62 } else {
63 parsed.as_secs() as u128
64 };
65 let coerced = match isize::try_from(amount) {
66 Ok(coerced) => coerced,
67 Err(_) => {
68 return Err(Error::new(ErrorKind::NotConvertible {
69 target: ValueType::Int,
70 found: ValueType::String,
71 }));
72 }
73 };
74
75 *value = Value::Int(coerced);
76 Ok(())
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 #[test]
85 fn coerces_to_seconds() {
86 let mut value = Value::String("1h30m".into());
87 Duration::new().validate(&mut value).unwrap();
88 assert_eq!(value, Value::Int(5400));
89 }
90
91 #[test]
92 fn coerces_to_millis() {
93 let mut value = Value::String("250ms".into());
94 Duration::new().millis().validate(&mut value).unwrap();
95 assert_eq!(value, Value::Int(250));
96 }
97
98 #[test]
99 fn rejects_garbage() {
100 let mut value = Value::String("soon".into());
101 assert!(Duration::new().validate(&mut value).is_err());
102 }
103}