use toml::Value;
use data::Container;
use enums::{ExtractResult, FormatDesc};
use item_def::ItemDef;
use item_value::ItemValue;
use toml_def::TomlDef;
use data::duration_decoder;
use value::I64Value;
pub struct ItemDuration
{
name : String,
min : Option<String>,
max : Option<String>,
optional : bool,
default : Option<i64>
}
impl ItemDuration
{
pub fn with_name<T:AsRef<str>>(name : T) -> ItemDuration
{
ItemDuration {
name : String::from(name.as_ref()),
min : None,
max : None,
optional : false,
default : None
}
}
pub fn add_to(
self,
group : &mut TomlDef
) -> I64Value
{
let result = I64Value::new();
group.add_notify(self.name.clone(), Box::new(result.clone()));
group.ref_add(self);
result
}
pub fn min(
mut self,
min : &str
) -> Self
{
if let Some(val) = duration_decoder::process(min)
{
if let Some(max) = self.max.as_ref().map(|x| duration_decoder::process(x).unwrap())
{
if max < val
{
panic!("Mininum value [{}] is greater than the maximum value [{}]", min, self.max.as_ref().unwrap());
}
}
self.min = Some(String::from(min));
}
else
{
panic!("Could not decode [{}]", min);
}
self
}
pub fn max<T:AsRef<str>>(
mut self,
max : T
) -> Self
{
if let Some(val) = duration_decoder::process(max.as_ref())
{
if let Some(min) = self.min.as_ref().map(|x| duration_decoder::process(x).unwrap())
{
if min > val
{
panic!("Maximum value [{}] is less than the minimum value [{}]", max.as_ref(), self.min.as_ref().unwrap());
}
}
self.max = Some(String::from(max.as_ref()));
}
else
{
panic!("Could not decode [{}]", max.as_ref());
}
self
}
pub fn optional(
mut self,
) -> Self
{
self.optional = true;
self
}
pub fn default<T:AsRef<str>>(
mut self,
default : T
) -> Self
{
if let Some(val) = duration_decoder::process(default.as_ref())
{
self.default = Some(val);
}
else
{
panic!("Could not decode [{}]", default.as_ref());
}
self
}
}
impl ItemDef for ItemDuration
{
fn name(&self) -> &str
{
&self.name
}
fn extract(
&self,
value : &Value
) -> ExtractResult
{
if let Some(value) = value.as_str()
{
if let Some(val) = duration_decoder::process(value)
{
if let Some(ref min) = self.min
{
if val < duration_decoder::process(min).unwrap()
{
return ExtractResult::duration_underrun(self.min.clone(), self.max.clone())
}
}
if let Some(ref max) = self.max
{
if val > duration_decoder::process(max).unwrap()
{
return ExtractResult::duration_overflow(self.min.clone(), self.max.clone())
}
}
ExtractResult::Item(ItemValue::Duration(val))
}
else
{
ExtractResult::cannot_parse(Some(FormatDesc::Duration))
}
}
else
{
ExtractResult::incorrect_type("string")
}
}
fn is_optional(&self) -> bool
{
self.optional
}
fn default(&self) -> Option<ItemValue>
{
self.default.map(|x|ItemValue::Duration(x))
}
}
#[cfg(test)]
mod tests
{
use toml::Value;
use item_def::ItemDef;
use item_value::ItemValue;
use enums::{ExtractResult, ValidationError};
macro_rules! test {
($item:expr, $val:expr) => ($item.extract(&Value::String(String::from($val))))
}
#[test]
fn set()
{
super::ItemDuration::with_name("a").min("12m");
super::ItemDuration::with_name("b").max("67h");
super::ItemDuration::with_name("c").default("123ms");
}
#[test]
fn min()
{
let test = super::ItemDuration::with_name("b").min("3h 5m");
assert_duration_underrun!(test!(test, "-65m"), "3h 5m");
assert_duration_underrun!(test!(test, "2h"), "3h 5m");
assert_duration!(test!(test, "3h 5m"), 3, 5, 0, 0);
assert_duration!(test!(test, "3h 5m 20ms"), 3, 5, 0, 20);
}
#[test]
fn max()
{
let test = super::ItemDuration::with_name("b").max("3h 5m");
assert_duration!(test!(test, "-65m"), -65, 0, 0);
assert_duration!(test!(test, "2h"), 2, 0, 0, 0);
assert_duration!(test!(test, "3h 5m"), 3, 5, 0, 0);
assert_duration_overflow!(test!(test, "3h 5m 20ms"), "3h 5m");
}
#[test]
fn min_max()
{
let test = super::ItemDuration::with_name("b").min("2h 7m").max("3h 5m");
assert_duration_underrun!(test!(test, "-65m"), "2h 7m", "3h 5m");
assert_duration_underrun!(test!(test, "2h"), "2h 7m", "3h 5m");
assert_duration!(test!(test, "2h 7m"), 2, 7, 0, 0);
assert_duration!(test!(test, "3h 1s"), 3, 0, 1, 0);
assert_duration!(test!(test, "3h 5m"), 3, 5, 0, 0);
assert_duration_overflow!(test!(test, "3h 5m 20ms"), "2h 7m", "3h 5m");
}
#[test]
fn validate()
{
let test = super::ItemDuration::with_name("b");
assert_incorrect_type!(test.extract(&Value::Integer(27)));
assert_incorrect_type!(test.extract(&Value::Boolean(true)));
assert_cannot_parse!(test.extract(&Value::String(String::from("65"))));
assert_cannot_parse!(test.extract(&Value::String(String::from("abc"))));
}
}