tanzim_validate/
number.rs1use crate::error::{Error, ErrorKind};
2use crate::{Meta, Validator};
3use tanzim_value::Value;
4
5#[derive(Debug, Clone, Copy)]
7pub(crate) enum Sign {
8 Positive,
9 NonNegative,
10 Negative,
11 NonPositive,
12}
13
14pub(crate) fn check_sign(sign: Option<Sign>, value: f64) -> Result<(), Error> {
16 let (ok, expected) = match sign {
17 Some(Sign::Positive) => (value > 0.0, "positive number"),
18 Some(Sign::NonNegative) => (value >= 0.0, "non-negative number"),
19 Some(Sign::Negative) => (value < 0.0, "negative number"),
20 Some(Sign::NonPositive) => (value <= 0.0, "non-positive number"),
21 None => (true, ""),
22 };
23 if ok {
24 Ok(())
25 } else {
26 Err(Error::new(ErrorKind::Format { expected }))
27 }
28}
29
30#[derive(Debug, Clone, Default)]
36pub struct Number {
37 meta: Meta,
38 min: Option<f64>,
39 max: Option<f64>,
40 sign: Option<Sign>,
41}
42
43impl Number {
44 pub fn with_meta(mut self, meta: Meta) -> Self {
46 self.meta = meta;
47 self
48 }
49
50 pub fn new() -> Self {
51 Self::default()
52 }
53
54 pub fn min(mut self, min: f64) -> Self {
55 self.min = Some(min);
56 self
57 }
58
59 pub fn max(mut self, max: f64) -> Self {
60 self.max = Some(max);
61 self
62 }
63
64 pub fn range(mut self, start: f64, end: f64) -> Self {
65 self.min = Some(start);
66 self.max = Some(end);
67 self
68 }
69
70 pub fn positive(mut self) -> Self {
72 self.sign = Some(Sign::Positive);
73 self
74 }
75
76 pub fn non_negative(mut self) -> Self {
78 self.sign = Some(Sign::NonNegative);
79 self
80 }
81
82 pub fn negative(mut self) -> Self {
84 self.sign = Some(Sign::Negative);
85 self
86 }
87
88 pub fn non_positive(mut self) -> Self {
90 self.sign = Some(Sign::NonPositive);
91 self
92 }
93}
94
95impl Validator for Number {
96 fn meta(&self) -> &Meta {
97 &self.meta
98 }
99
100 fn meta_mut(&mut self) -> &mut Meta {
101 &mut self.meta
102 }
103
104 fn check(&self, value: &mut Value) -> Result<(), Error> {
105 let number = match value {
106 Value::Int(number) => *number as f64,
107 Value::Float(number) => *number,
108 _ => return Err(Error::new(ErrorKind::Format { expected: "number" })),
109 };
110
111 if let Some(min) = self.min
112 && number < min
113 {
114 return Err(Error::new(ErrorKind::BelowMin {
115 value: number.to_string(),
116 min: min.to_string(),
117 }));
118 }
119 if let Some(max) = self.max
120 && number > max
121 {
122 return Err(Error::new(ErrorKind::AboveMax {
123 value: number.to_string(),
124 max: max.to_string(),
125 }));
126 }
127
128 check_sign(self.sign, number)
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135
136 #[test]
137 fn accepts_int_and_float_without_converting() {
138 let mut int_value = Value::Int(3);
139 Number::new().validate(&mut int_value).unwrap();
140 assert_eq!(int_value, Value::Int(3));
141
142 let mut float_value = Value::Float(3.5);
143 Number::new().validate(&mut float_value).unwrap();
144 assert_eq!(float_value, Value::Float(3.5));
145 }
146
147 #[test]
148 fn rejects_non_numbers() {
149 assert!(
150 Number::new()
151 .validate(&mut Value::String("3".into()))
152 .is_err()
153 );
154 }
155
156 #[test]
157 fn bounds_and_sign() {
158 assert!(
159 Number::new()
160 .range(0.0, 10.0)
161 .validate(&mut Value::Int(5))
162 .is_ok()
163 );
164 assert!(
165 Number::new()
166 .range(0.0, 10.0)
167 .validate(&mut Value::Float(11.0))
168 .is_err()
169 );
170 assert!(
171 Number::new()
172 .positive()
173 .validate(&mut Value::Float(0.0))
174 .is_err()
175 );
176 assert!(
177 Number::new()
178 .non_negative()
179 .validate(&mut Value::Int(0))
180 .is_ok()
181 );
182 }
183}