use crate::error::{Error, ErrorKind};
use crate::{Meta, Validator};
use tanzim_value::{Value, ValueType};
#[derive(Debug, Clone, Default)]
pub struct Duration {
meta: Meta,
millis: bool,
}
impl Duration {
pub fn with_meta(mut self, meta: Meta) -> Self {
self.meta = meta;
self
}
pub fn new() -> Self {
Self::default()
}
pub fn millis(mut self) -> Self {
self.millis = true;
self
}
}
impl Validator for Duration {
fn meta(&self) -> &Meta {
&self.meta
}
fn meta_mut(&mut self) -> &mut Meta {
&mut self.meta
}
fn check(&self, value: &mut Value) -> Result<(), Error> {
let text = match value {
Value::String(text) => text,
other => {
return Err(Error::new(ErrorKind::Type {
expected: ValueType::String,
found: other.type_name(),
}));
}
};
let parsed = match humantime::parse_duration(text) {
Ok(parsed) => parsed,
Err(_) => {
return Err(Error::new(ErrorKind::Format {
expected: "duration",
}));
}
};
let amount = if self.millis {
parsed.as_millis()
} else {
parsed.as_secs() as u128
};
let coerced = match isize::try_from(amount) {
Ok(coerced) => coerced,
Err(_) => {
return Err(Error::new(ErrorKind::NotConvertible {
target: ValueType::Int,
found: ValueType::String,
}));
}
};
*value = Value::Int(coerced);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn coerces_to_seconds() {
let mut value = Value::String("1h30m".into());
Duration::new().validate(&mut value).unwrap();
assert_eq!(value, Value::Int(5400));
}
#[test]
fn coerces_to_millis() {
let mut value = Value::String("250ms".into());
Duration::new().millis().validate(&mut value).unwrap();
assert_eq!(value, Value::Int(250));
}
#[test]
fn rejects_garbage() {
let mut value = Value::String("soon".into());
assert!(Duration::new().validate(&mut value).is_err());
}
}