use errors::{TeraResult, TeraError};
use serde_json::value::{Value};
use context::ValueNumber;
pub type TesterFn = fn(&str, Option<Value>, Vec<Value>) -> TeraResult<bool>;
fn number_args_allowed(arg_name: &str, tester_name: &str, max: usize, args_len: usize) -> TeraResult<()> {
if max == 0 && args_len > max {
return Err(TeraError::TestError(
tester_name.to_string(),
format!(
"{} was called on the variable {} with some arguments \
but this test doesn't take arguments.",
tester_name, arg_name
)
))
}
if args_len > max {
return Err(TeraError::TestError(
tester_name.to_string(),
format!(
"{} was called on the variable {} with {} arguments, the max number is {}. ",
tester_name, arg_name, args_len, max
)
))
}
Ok(())
}
fn value_defined(arg_name: &str, tester_name: &str, value: &Option<Value>) -> TeraResult<()> {
if value.is_none() {
return Err(TeraError::TestError(
tester_name.to_string(),
format!("{} was called on the variable {}, which is undefined", tester_name, arg_name)
));
}
Ok(())
}
pub fn defined(name: &str, value: Option<Value>, params: Vec<Value>) -> TeraResult<bool> {
try!(number_args_allowed(name, "defined", 0, params.len()));
Ok(value.is_some())
}
pub fn undefined(name: &str, value: Option<Value>, params: Vec<Value>) -> TeraResult<bool> {
try!(number_args_allowed(name, "undefined", 0, params.len()));
Ok(value.is_none())
}
pub fn string(name: &str, value: Option<Value>, params: Vec<Value>) -> TeraResult<bool> {
try!(number_args_allowed(name, "string", 0, params.len()));
try!(value_defined(name, "string", &value));
match value {
Some(Value::String(_)) => Ok(true),
_ => Ok(false)
}
}
pub fn number(name: &str, value: Option<Value>, params: Vec<Value>) -> TeraResult<bool> {
try!(number_args_allowed(name, "number", 0, params.len()));
try!(value_defined(name, "number", &value));
match value {
Some(Value::I64(_)) | Some(Value::F64(_)) | Some(Value::U64(_)) => Ok(true),
_ => Ok(false)
}
}
pub fn odd(name: &str, value: Option<Value>, params: Vec<Value>) -> TeraResult<bool> {
try!(number_args_allowed(name, "odd", 0, params.len()));
try!(value_defined(name, "odd", &value));
match value.and_then(|v| v.to_number().ok()) {
Some(f) => Ok(f % 2.0 != 0.0),
_ => Err(TeraError::TestError(
"odd".to_string(),
"odd can only be called on numbers".to_string()
))
}
}
pub fn even(name: &str, value: Option<Value>, params: Vec<Value>) -> TeraResult<bool> {
try!(number_args_allowed(name, "even", 0, params.len()));
try!(value_defined(name, "even", &value));
let is_odd = try!(odd(name, value, params));
Ok(!is_odd)
}
#[cfg(test)]
mod tests {
use super::{defined, string};
use serde_json::value::{to_value};
#[test]
fn test_number_args_ok() {
assert!(defined("", None, vec![]).is_ok())
}
#[test]
fn test_too_many_args() {
assert!(defined("", None, vec![to_value(1)]).is_err())
}
#[test]
fn test_value_defined() {
assert!(string("", None, vec![]).is_err())
}
}