1
use core::str::FromStr;
2

            
3
use crate::anystr::AnyStr;
4
use crate::parser::{ParseDelegate, Parser};
5
use crate::{Error, ErrorKind};
6

            
7
/// A JSON-encoded number.
8
55
#[derive(Debug, Eq, PartialEq, Clone)]
9
pub struct JsonNumber<'a> {
10
    /// The JSON source for this number.
11
    pub(crate) source: AnyStr<'a>,
12
}
13

            
14
impl<'a> JsonNumber<'a> {
15
    /// Parses `json`, expecting a single number value.
16
    ///
17
    /// # Errors
18
    ///
19
    /// Returns [`ErrorKind::ExpectedString`] if a non-string value is
20
    /// encountered.
21
38
    pub fn from_json(json: &'a str) -> Result<Self, Error> {
22
38
        Parser::parse_json(json, NumberParser).map_err(Error::into_infallable)
23
38
    }
24

            
25
    /// Returns the JSON-encoded representation of this number.
26
    #[must_use]
27
165
    pub fn source(&self) -> &str {
28
165
        self.source.as_ref()
29
165
    }
30

            
31
    /// Parses the contained value as an [`f32`], if possible.
32
    ///
33
    /// The JSON parser only validates that the number takes a correct form. If
34
    /// a number cannot be parsed by the underlying routine due to having too
35
    /// many digits, it this function can return None.
36
    #[must_use]
37
2
    pub fn as_f32(&self) -> Option<f32> {
38
2
        self.parse().ok()
39
2
    }
40

            
41
    /// Parses the contained value as an [`f64`], if possible.
42
    ///
43
    /// The JSON parser only validates that the number takes a correct form. If
44
    /// a number cannot be parsed by the underlying routine due to having too
45
    /// many digits, it this function can return None.
46
    #[must_use]
47
3
    pub fn as_f64(&self) -> Option<f64> {
48
3
        self.parse().ok()
49
3
    }
50

            
51
    /// Parses the contained value.
52
32
    pub fn parse<T: FromStr>(&self) -> Result<T, T::Err> {
53
32
        self.source().parse()
54
32
    }
55
}
56

            
57
macro_rules! impl_as_number {
58
    ($name:ident, $type:ident) => {
59
        impl JsonNumber<'_> {
60
            /// Parses the contained value as an
61
            #[doc = concat!("[`", stringify!($type), "`]")]
62
            /// if possible.
63
            ///
64
            /// If the source number is a floating point number or has a negative sign,
65
            /// this will always return None.
66
            #[must_use]
67
27
            pub fn $name(&self) -> Option<$type> {
68
27
                self.parse().ok()
69
27
            }
70
        }
71
    };
72
}
73

            
74
impl_as_number!(as_u8, u8);
75
impl_as_number!(as_u16, u16);
76
impl_as_number!(as_u32, u32);
77
impl_as_number!(as_u64, u64);
78
impl_as_number!(as_u128, u128);
79
impl_as_number!(as_usize, usize);
80
impl_as_number!(as_i8, i8);
81
impl_as_number!(as_i16, i16);
82
impl_as_number!(as_i32, i32);
83
impl_as_number!(as_i64, i64);
84
impl_as_number!(as_i128, i128);
85
impl_as_number!(as_isize, isize);
86

            
87
struct NumberParser;
88

            
89
impl<'a> ParseDelegate<'a> for NumberParser {
90
    type Array = ();
91
    type Error = ErrorKind;
92
    type Key = ();
93
    type Object = ();
94
    type Value = JsonNumber<'a>;
95

            
96
1
    fn null(&mut self) -> Result<Self::Value, Self::Error> {
97
1
        Err(ErrorKind::ExpectedNumber)
98
1
    }
99

            
100
2
    fn boolean(&mut self, _value: bool) -> Result<Self::Value, Self::Error> {
101
2
        Err(ErrorKind::ExpectedNumber)
102
2
    }
103

            
104
32
    fn number(&mut self, value: JsonNumber<'a>) -> Result<Self::Value, Self::Error> {
105
32
        Ok(value)
106
32
    }
107

            
108
1
    fn string(&mut self, _value: crate::JsonString<'a>) -> Result<Self::Value, Self::Error> {
109
1
        Err(ErrorKind::ExpectedNumber)
110
1
    }
111

            
112
1
    fn begin_object(&mut self) -> Result<Self::Object, Self::Error> {
113
1
        Err(ErrorKind::ExpectedNumber)
114
1
    }
115

            
116
    fn object_key(
117
        &mut self,
118
        _object: &mut Self::Object,
119
        _key: crate::JsonString<'a>,
120
    ) -> Result<Self::Key, Self::Error> {
121
        unreachable!("error returned from begin_object")
122
    }
123

            
124
    fn object_value(
125
        &mut self,
126
        _object: &mut Self::Object,
127
        _key: Self::Key,
128
        _value: Self::Value,
129
    ) -> Result<(), Self::Error> {
130
        unreachable!("error returned from begin_object")
131
    }
132

            
133
    fn object_is_empty(&self, _object: &Self::Object) -> bool {
134
        unreachable!("error returned from begin_object")
135
    }
136

            
137
    fn end_object(&mut self, _object: Self::Object) -> Result<Self::Value, Self::Error> {
138
        unreachable!("error returned from begin_object")
139
    }
140

            
141
1
    fn begin_array(&mut self) -> Result<Self::Array, Self::Error> {
142
1
        Err(ErrorKind::ExpectedNumber)
143
1
    }
144

            
145
    fn array_value(
146
        &mut self,
147
        _array: &mut Self::Array,
148
        _value: Self::Value,
149
    ) -> Result<(), Self::Error> {
150
        unreachable!("error returned from array_value")
151
    }
152

            
153
    fn array_is_empty(&self, _array: &Self::Array) -> bool {
154
        unreachable!("error returned from array_value")
155
    }
156

            
157
    fn end_array(&mut self, _array: Self::Array) -> Result<Self::Value, Self::Error> {
158
        unreachable!("error returned from array_value")
159
    }
160

            
161
    fn kind_of(&self, _value: &Self::Value) -> crate::parser::JsonKind {
162
        unreachable!("allow_all_types_at_root is always true")
163
    }
164
}
165

            
166
1
#[test]
167
#[cfg(feature = "alloc")]
168
1
fn json_number_from_json() {
169
1
    assert_eq!(
170
1
        JsonNumber::from_json("1").unwrap(),
171
1
        JsonNumber {
172
1
            source: AnyStr::Borrowed("1")
173
1
        }
174
1
    );
175

            
176
1
    let expected_number = JsonNumber::from_json("true")
177
1
        .expect_err("shouldn't allow non-numbers")
178
1
        .kind;
179
1
    assert!(matches!(expected_number, ErrorKind::ExpectedNumber));
180
1
}
181

            
182
1
#[test]
183
#[cfg(feature = "alloc")]
184
1
fn json_number_conversions() {
185
1
    let one = JsonNumber::from_json("1").unwrap();
186
1
    assert_eq!(one.as_i64().unwrap(), 1);
187
1
    assert_eq!(one.as_u64().unwrap(), 1);
188
1
    assert!((one.as_f64().unwrap() - 1.0).abs() < f64::EPSILON);
189
1
}
190

            
191
1
#[test]
192
1
fn from_json_bad_types() {
193
1
    assert_eq!(
194
1
        JsonNumber::from_json("\"\"").unwrap_err().kind,
195
1
        ErrorKind::ExpectedNumber
196
1
    );
197
1
    assert_eq!(
198
1
        JsonNumber::from_json("null").unwrap_err().kind,
199
1
        ErrorKind::ExpectedNumber
200
1
    );
201
1
    assert_eq!(
202
1
        JsonNumber::from_json("true").unwrap_err().kind,
203
1
        ErrorKind::ExpectedNumber
204
1
    );
205
1
    assert_eq!(
206
1
        JsonNumber::from_json("[]").unwrap_err().kind,
207
1
        ErrorKind::ExpectedNumber
208
1
    );
209
1
    assert_eq!(
210
1
        JsonNumber::from_json("{}").unwrap_err().kind,
211
1
        ErrorKind::ExpectedNumber
212
1
    );
213
1
}
214

            
215
1
#[test]
216
#[cfg(feature = "alloc")]
217
1
fn as_es() {
218
1
    macro_rules! test_as {
219
1
        ($as:ident) => {
220
1
            assert_eq!(JsonNumber::from_json("1").unwrap().$as(), Some(1));
221
1
        };
222
1
    }
223
1

            
224
1
    test_as!(as_i8);
225
1
    test_as!(as_i16);
226
1
    test_as!(as_i32);
227
1
    test_as!(as_i64);
228
1
    test_as!(as_i128);
229
1
    test_as!(as_isize);
230
1
    test_as!(as_u8);
231
1
    test_as!(as_u16);
232
1
    test_as!(as_u32);
233
1
    test_as!(as_u64);
234
1
    test_as!(as_u128);
235
1
    test_as!(as_usize);
236

            
237
1
    assert!(JsonNumber::from_json("0").unwrap().as_f32().unwrap().abs() < f32::EPSILON);
238
1
    assert!(JsonNumber::from_json("0").unwrap().as_f64().unwrap().abs() < f64::EPSILON);
239
1
}