json_matcher/matchers/
number.rs

1use serde_json::Value;
2
3use crate::{JsonMatcher, JsonMatcherError};
4
5pub struct IntegerMatcher {
6    value: i64,
7}
8
9impl IntegerMatcher {
10    pub fn new(value: i64) -> Self {
11        Self { value }
12    }
13}
14
15impl JsonMatcher for IntegerMatcher {
16    fn json_matches(&self, value: &Value) -> Vec<JsonMatcherError> {
17        match value {
18            Value::Number(num) => {
19                let Some(actual) = num.as_i64() else {
20                    return vec![JsonMatcherError::at_root(format!(
21                        "Expected integer {} but got float {}",
22                        self.value, num
23                    ))];
24                };
25                if actual == self.value {
26                    vec![]
27                } else {
28                    vec![JsonMatcherError::at_root(format!(
29                        "Expected integer {} but got {}",
30                        self.value, actual
31                    ))]
32                }
33            }
34            _ => vec![JsonMatcherError::at_root("Value is not an integer")],
35        }
36    }
37}
38
39impl JsonMatcher for i8 {
40    fn json_matches(&self, value: &Value) -> Vec<JsonMatcherError> {
41        IntegerMatcher::new(*self as i64).json_matches(value)
42    }
43}
44
45impl JsonMatcher for i16 {
46    fn json_matches(&self, value: &Value) -> Vec<JsonMatcherError> {
47        IntegerMatcher::new(*self as i64).json_matches(value)
48    }
49}
50
51impl JsonMatcher for i32 {
52    fn json_matches(&self, value: &Value) -> Vec<JsonMatcherError> {
53        IntegerMatcher::new(*self as i64).json_matches(value)
54    }
55}
56
57impl JsonMatcher for i64 {
58    fn json_matches(&self, value: &Value) -> Vec<JsonMatcherError> {
59        IntegerMatcher::new(*self).json_matches(value)
60    }
61}
62
63impl JsonMatcher for u8 {
64    fn json_matches(&self, value: &Value) -> Vec<JsonMatcherError> {
65        IntegerMatcher::new(*self as i64).json_matches(value)
66    }
67}
68
69impl JsonMatcher for u16 {
70    fn json_matches(&self, value: &Value) -> Vec<JsonMatcherError> {
71        IntegerMatcher::new(*self as i64).json_matches(value)
72    }
73}
74
75impl JsonMatcher for u32 {
76    fn json_matches(&self, value: &Value) -> Vec<JsonMatcherError> {
77        IntegerMatcher::new(*self as i64).json_matches(value)
78    }
79}
80
81pub struct NumberMatcher {
82    number: f64,
83}
84
85impl NumberMatcher {
86    pub fn new(value: f64) -> Self {
87        Self { number: value }
88    }
89}
90
91impl JsonMatcher for NumberMatcher {
92    fn json_matches(&self, value: &Value) -> Vec<JsonMatcherError> {
93        match value {
94            Value::Number(num) => {
95                let Some(actual) = num.as_f64() else {
96                    return vec![JsonMatcherError::at_root(format!(
97                        "Expected float {} but got integer {}",
98                        self.number, num
99                    ))];
100                };
101                if actual == self.number {
102                    vec![]
103                } else {
104                    vec![JsonMatcherError::at_root(format!(
105                        "Expected float {} but got {}",
106                        self.number, actual
107                    ))]
108                }
109            }
110            _ => vec![JsonMatcherError::at_root("Value is not a float")],
111        }
112    }
113}
114
115impl JsonMatcher for f32 {
116    fn json_matches(&self, value: &Value) -> Vec<JsonMatcherError> {
117        NumberMatcher::new(*self as f64).json_matches(value)
118    }
119}
120
121impl JsonMatcher for f64 {
122    fn json_matches(&self, value: &Value) -> Vec<JsonMatcherError> {
123        NumberMatcher::new(*self).json_matches(value)
124    }
125}
126
127#[cfg(test)]
128mod tests {
129    use serde_json::Number;
130
131    use crate::assert_jm;
132
133    use crate::test::catch_string_panic;
134
135    use super::*;
136
137    #[test]
138    fn test_integer_matcher() {
139        let get_matcher = || IntegerMatcher::new(4);
140        assert_jm!(Value::Number(4.into()), get_matcher());
141        // not a number
142        assert_eq!(
143            catch_string_panic(|| assert_jm!(Value::String("bloop".to_string()), get_matcher())),
144            r#"
145Json matcher failed:
146  - $: Value is not an integer
147
148Actual:
149"bloop""#
150        );
151        // is number, but not an integer
152        assert_eq!(
153            catch_string_panic(|| assert_jm!(
154                Value::Number(Number::from_f64(2.2).unwrap()),
155                get_matcher()
156            )),
157            r#"
158Json matcher failed:
159  - $: Expected integer 4 but got float 2.2
160
161Actual:
1622.2"#
163        );
164        // is integer, but not expected value
165        assert_eq!(
166            catch_string_panic(|| assert_jm!(Value::Number(2.into()), get_matcher())),
167            r#"
168Json matcher failed:
169  - $: Expected integer 4 but got 2
170
171Actual:
1722"#
173        );
174    }
175
176    #[test]
177    fn test_number_matcher() {
178        let get_matcher = || NumberMatcher::new(4.0);
179        // is expected value as float
180        assert_jm!(Value::Number(Number::from_f64(4.0).unwrap()), get_matcher());
181        // is expected value as integer
182        assert_jm!(Value::Number(4i64.into()), get_matcher());
183        // not a number
184        assert_eq!(
185            catch_string_panic(|| assert_jm!(Value::String("bloop".to_string()), get_matcher())),
186            r#"
187Json matcher failed:
188  - $: Value is not a float
189
190Actual:
191"bloop""#
192        );
193        // is float, but not expected value
194        assert_eq!(
195            catch_string_panic(|| assert_jm!(
196                Value::Number(Number::from_f64(7.2).unwrap()),
197                get_matcher()
198            )),
199            r#"
200Json matcher failed:
201  - $: Expected float 4 but got 7.2
202
203Actual:
2047.2"#
205        );
206    }
207
208    #[test]
209    fn test_raw_implementations() {
210        // i8
211        assert_eq!(4i8.json_matches(&Value::Number(4.into())), vec![]);
212        assert_eq!(
213            4i8.json_matches(&Value::Number(5.into()))
214                .into_iter()
215                .map(|e| e.to_string())
216                .collect::<String>(),
217            "$: Expected integer 4 but got 5"
218        );
219        // i16
220        assert_eq!(4i16.json_matches(&Value::Number(4.into())), vec![]);
221        assert_eq!(
222            4i16.json_matches(&Value::Number(5.into()))
223                .into_iter()
224                .map(|e| e.to_string())
225                .collect::<String>(),
226            "$: Expected integer 4 but got 5"
227        );
228        // i32
229        assert_eq!(4i32.json_matches(&Value::Number(4.into())), vec![]);
230        assert_eq!(
231            4i32.json_matches(&Value::Number(5.into()))
232                .into_iter()
233                .map(|e| e.to_string())
234                .collect::<String>(),
235            "$: Expected integer 4 but got 5"
236        );
237        // i64
238        assert_eq!(4i64.json_matches(&Value::Number(4.into())), vec![]);
239        assert_eq!(
240            4i64.json_matches(&Value::Number(5.into()))
241                .into_iter()
242                .map(|e| e.to_string())
243                .collect::<String>(),
244            "$: Expected integer 4 but got 5"
245        );
246        // u8
247        assert_eq!(4u8.json_matches(&Value::Number(4.into())), vec![]);
248        assert_eq!(
249            4u8.json_matches(&Value::Number(5.into()))
250                .into_iter()
251                .map(|e| e.to_string())
252                .collect::<String>(),
253            "$: Expected integer 4 but got 5"
254        );
255        // u16
256        assert_eq!(4u16.json_matches(&Value::Number(4.into())), vec![]);
257        assert_eq!(
258            4u16.json_matches(&Value::Number(5.into()))
259                .into_iter()
260                .map(|e| e.to_string())
261                .collect::<String>(),
262            "$: Expected integer 4 but got 5"
263        );
264        // u32
265        assert_eq!(4u32.json_matches(&Value::Number(4.into())), vec![]);
266        assert_eq!(
267            4u32.json_matches(&Value::Number(5.into()))
268                .into_iter()
269                .map(|e| e.to_string())
270                .collect::<String>(),
271            "$: Expected integer 4 but got 5"
272        );
273        // f32
274        assert_eq!(4f32.json_matches(&Value::Number(4.into())), vec![]);
275        assert_eq!(
276            4f32.json_matches(&Value::Number(5.into()))
277                .into_iter()
278                .map(|e| e.to_string())
279                .collect::<String>(),
280            "$: Expected float 4 but got 5"
281        );
282        // f64
283        assert_eq!(4f64.json_matches(&Value::Number(4.into())), vec![]);
284        assert_eq!(
285            4f64.json_matches(&Value::Number(5.into()))
286                .into_iter()
287                .map(|e| e.to_string())
288                .collect::<String>(),
289            "$: Expected float 4 but got 5"
290        );
291    }
292}