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
95crate::impl_meta_methods!(Number);
96
97impl Validator for Number {
98 fn meta(&self) -> &Meta {
99 &self.meta
100 }
101
102 fn meta_mut(&mut self) -> &mut Meta {
103 &mut self.meta
104 }
105
106 fn check(&self, value: &mut Value) -> Result<(), Error> {
107 let number = match value {
108 Value::Int(number) => *number as f64,
109 Value::Float(number) => *number,
110 _ => return Err(Error::new(ErrorKind::Format { expected: "number" })),
111 };
112
113 if let Some(min) = self.min
114 && number < min
115 {
116 return Err(Error::new(ErrorKind::BelowMin {
117 value: number.to_string(),
118 min: min.to_string(),
119 }));
120 }
121 if let Some(max) = self.max
122 && number > max
123 {
124 return Err(Error::new(ErrorKind::AboveMax {
125 value: number.to_string(),
126 max: max.to_string(),
127 }));
128 }
129
130 check_sign(self.sign, number)
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137
138 #[test]
139 fn accepts_int_and_float_without_converting() {
140 let mut int_value = Value::Int(3);
141 Number::new().validate(&mut int_value).unwrap();
142 assert_eq!(int_value, Value::Int(3));
143
144 let mut float_value = Value::Float(3.5);
145 Number::new().validate(&mut float_value).unwrap();
146 assert_eq!(float_value, Value::Float(3.5));
147 }
148
149 #[test]
150 fn rejects_non_numbers() {
151 assert!(
152 Number::new()
153 .validate(&mut Value::String("3".into()))
154 .is_err()
155 );
156 }
157
158 #[test]
159 fn bounds_and_sign() {
160 assert!(
161 Number::new()
162 .range(0.0, 10.0)
163 .validate(&mut Value::Int(5))
164 .is_ok()
165 );
166 assert!(
167 Number::new()
168 .range(0.0, 10.0)
169 .validate(&mut Value::Float(11.0))
170 .is_err()
171 );
172 assert!(
173 Number::new()
174 .positive()
175 .validate(&mut Value::Float(0.0))
176 .is_err()
177 );
178 assert!(
179 Number::new()
180 .non_negative()
181 .validate(&mut Value::Int(0))
182 .is_ok()
183 );
184 }
185}