use crate::Validator;
use crate::error::{Error, ErrorKind};
use crate::number::{Sign, check_sign};
use tanzim_value::{Value, ValueType};
#[derive(Debug, Clone, Default)]
pub struct Float {
min: Option<f64>,
max: Option<f64>,
sign: Option<Sign>,
}
impl Float {
pub fn new() -> Self {
Self::default()
}
pub fn min(mut self, min: f64) -> Self {
self.min = Some(min);
self
}
pub fn max(mut self, max: f64) -> Self {
self.max = Some(max);
self
}
pub fn range(mut self, start: f64, end: f64) -> Self {
self.min = Some(start);
self.max = Some(end);
self
}
pub fn positive(mut self) -> Self {
self.sign = Some(Sign::Positive);
self
}
pub fn non_negative(mut self) -> Self {
self.sign = Some(Sign::NonNegative);
self
}
pub fn negative(mut self) -> Self {
self.sign = Some(Sign::Negative);
self
}
pub fn non_positive(mut self) -> Self {
self.sign = Some(Sign::NonPositive);
self
}
}
impl Validator for Float {
fn validate(&self, value: &mut Value) -> Result<(), Error> {
let coerced = match value {
Value::Float(number) => *number,
Value::Int(number) => *number as f64,
Value::String(text) => match text.parse::<f64>() {
Ok(number) => number,
Err(_) => {
return Err(Error::new(ErrorKind::NotConvertible {
target: ValueType::Float,
found: ValueType::String,
}));
}
},
other => {
return Err(Error::new(ErrorKind::Type {
expected: ValueType::Float,
found: other.type_name(),
}));
}
};
if let Some(min) = self.min
&& coerced < min
{
return Err(Error::new(ErrorKind::BelowMin {
value: coerced.to_string(),
min: min.to_string(),
}));
}
if let Some(max) = self.max
&& coerced > max
{
return Err(Error::new(ErrorKind::AboveMax {
value: coerced.to_string(),
max: max.to_string(),
}));
}
check_sign(self.sign, coerced)?;
*value = Value::Float(coerced);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn accepts_float() {
let mut value = Value::Float(1.5);
assert!(Float::new().validate(&mut value).is_ok());
}
#[test]
fn coerces_integer() {
let mut value = Value::Int(7);
Float::new().validate(&mut value).unwrap();
assert_eq!(value, Value::Float(7.0));
}
#[test]
fn coerces_string() {
let mut value = Value::String("1.5".into());
Float::new().validate(&mut value).unwrap();
assert_eq!(value, Value::Float(1.5));
}
#[test]
fn enforces_range() {
let mut value = Value::Float(-0.1);
let error = Float::new()
.range(0.0, 1.0)
.validate(&mut value)
.unwrap_err();
assert!(matches!(error.kind, ErrorKind::BelowMin { .. }));
}
}