use std;
use toml::Value;
use data::Container;
use enums::ExtractResult;
use item_def::ItemDef;
use item_value::ItemValue;
use toml_def::TomlDef;
use value::UsizeValue;
pub struct ItemUsize
{
name : String,
min : Option<i64>,
max : Option<i64>,
optional : bool,
default : Option<usize>
}
impl ItemUsize
{
pub fn with_name<T:AsRef<str>>(name : T) -> ItemUsize
{
ItemUsize {
name : String::from(name.as_ref()),
min : None,
max : None,
optional : false,
default : None
}
}
pub fn add_to(
self,
group : &mut TomlDef
) -> UsizeValue
{
let notify = UsizeValue::new();
group.add_notify(self.name.clone(), Box::new(notify.clone()));
group.ref_add(self);
notify
}
pub fn min(
mut self,
min : usize
) -> Self
{
if min as u64 > std::i64::MAX as u64
{
panic!("Largest value that can be read from the file is i64::MAX.");
}
if let Some(max) = self.max
{
if max < min as i64
{
panic!("Minimum value [{}] is greater than the maximum value [{}]", min, max);
}
}
self.min = Some(min as i64);
self
}
pub fn max(
mut self,
max : usize
) -> Self
{
if max as u64 > std::i64::MAX as u64
{
panic!("Largest value that can be read from the file is i64::MAX.");
}
if let Some(min) = self.min
{
if min > max as i64
{
panic!("Maximum value [{}] is less than the minimum value [{}]", max, min);
}
}
self.max = Some(max as i64);
self
}
pub fn optional(
mut self,
) -> Self
{
self.optional = true;
self
}
pub fn default(
mut self,
default : usize
) -> Self
{
if default as u64 > std::i64::MAX as u64
{
panic!("Largest value that can be read from the file is i64::MAX.");
}
self.default = Some(default);
self
}
}
impl ItemDef for ItemUsize
{
fn name(&self) -> &str
{
&self.name
}
fn extract(
&self,
value : &Value
) -> ExtractResult
{
if let Some(value) = value.as_integer()
{
if let Some(min) = self.min
{
if value < min
{
return ExtractResult::underrun(self.min.clone(), self.max.clone())
}
}
else if value < std::usize::MIN as i64
{
return ExtractResult::underrun(Some(0), self.max.clone())
}
if let Some(max) = self.max
{
if value > max
{
return ExtractResult::overrun(self.min.clone(), self.max.clone())
}
}
ExtractResult::Item(ItemValue::Usize(value as usize))
}
else
{
ExtractResult::incorrect_type("number")
}
}
fn is_optional(&self) -> bool
{
self.optional
}
fn default(&self) -> Option<ItemValue>
{
self.default.as_ref().map(|x|ItemValue::Usize(*x))
}
}
#[cfg(test)]
mod tests
{
use std;
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::Integer($val)))
}
#[test]
fn set()
{
super::ItemUsize::with_name("a").min(12);
super::ItemUsize::with_name("b").max(67);
super::ItemUsize::with_name("c").default(123);
}
#[test]
fn min()
{
let test = super::ItemUsize::with_name("b").min(53);
assert_underrun!(test!(test, 17), 53);
assert_usize!(test!(test, 53), 53);
assert_usize!(test!(test, 67), 67);
}
#[test]
fn max()
{
let test = super::ItemUsize::with_name("b").max(24);
assert_usize!(test!(test, 3), 3);
assert_usize!(test!(test, 24), 24);
assert_overflow!(test!(test, 67), 24);
}
#[test]
fn min_max()
{
let test = super::ItemUsize::with_name("b").min(11).max(17);
assert_underrun!(test!(test, 0), 11, 17);
assert_usize!(test!(test, 11), 11);
assert_usize!(test!(test, 15), 15);
assert_usize!(test!(test, 17), 17);
assert_overflow!(test.extract(&Value::Integer(27)), 11, 17)
}
#[test]
fn validate()
{
let test = super::ItemUsize::with_name("b");
assert_usize!(test.extract(&Value::Integer(std::usize::MIN as i64)), std::usize::MIN as usize);
assert_usize!(test.extract(&Value::Integer(std::i64::MAX)), std::i64::MAX as usize);
assert_underrun!(test.extract(&Value::Integer(std::usize::MIN as i64 - 1)), 0);
}
}