rsonpath_syntax/
num.rs

1//! JSON number types expressible in a JSONPath query.
2//!
3//! Exposes the [`JsonInt`] and [`JsonUInt`] types
4//! that can represent any numbers in the range [-2<sup>53</sup>+1, 2<sup>53</sup>-1],
5//! with the unsigned version additionally guaranteed to be non-negative. All operations
6//! implemented are automatically checked for over- and underflow.
7//!
8//! This is governed by the [I-JSON IETF specification](https://www.rfc-editor.org/rfc/rfc7493.html#section-2).
9//! All numbers appearing in a JSONPath query are required to be I-JSON conformant
10//! (see [RFC 2.1-4.1](https://www.ietf.org/archive/id/draft-ietf-jsonpath-base-21.html#section-2.1-4.1)).
11//! This includes index values, all values in slice selectors, and constants
12//! in filter comparison expressions.
13//!
14//! # Examples
15//! ```
16//! # use rsonpath_syntax::num::{JsonInt, JsonUInt};
17//! // An i32/u32 converts directly to JsonInt/JsonUInt.
18//! let a = JsonInt::from(-42);
19//! let b = JsonUInt::from(42);
20//! // i64/u64 has to be checked for overflow.
21//! let c = JsonInt::try_from(42_000_000_000_000_i64).expect("within range");
22//! let d = JsonInt::try_from(42_000_000_000_000_000_i64).expect_err("too large");
23//!
24//! assert_eq!(a.as_i64(), -42);
25//! assert_eq!(b.as_u64(), 42);
26//! assert_eq!(c.as_i64(), 42_000_000_000_000_i64);
27//! ```
28pub mod error;
29
30use crate::num::error::{JsonFloatConvertError, JsonFloatParseError, JsonIntOverflowError, JsonIntParseError};
31use std::{
32    fmt::{self, Display, Formatter},
33    num::{NonZeroU32, NonZeroU64},
34    str::FromStr,
35};
36
37/// Signed interoperable JSON integer.
38///
39/// Provides an [IETF-conforming integer value](https://www.rfc-editor.org/rfc/rfc7493.html#section-2)
40/// Values are \[-2<sup>53</sup>+1, 2<sup>53</sup>-1].
41///
42/// All values in a JSONPath query are limited to this range for interoperability
43/// (see [RFC 2.1-4.1](https://www.ietf.org/archive/id/draft-ietf-jsonpath-base-21.html#section-2.1-4.1)).
44///
45/// The unsigned version is [`JsonUInt`].
46///
47/// # Examples
48/// ```
49/// # use rsonpath_syntax::num::JsonInt;
50/// let two = JsonInt::from(2);
51/// let zero = JsonInt::from(0);
52/// let negative = JsonInt::from(-2);
53///
54/// assert_eq!(two.as_i64(), 2);
55/// assert_eq!(zero.as_i64(), 0);
56/// assert_eq!(negative.as_i64(), -2);
57///
58/// let too_big = JsonInt::try_from(1_i64 << 53).expect_err("out of range");
59/// let too_small = JsonInt::try_from(-(1_i64 << 53)).expect_err("out of range");
60/// ```
61#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
62#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
63pub struct JsonInt(i64);
64
65/// Unsigned interoperable JSON integer.
66///
67/// Provides an [IETF-conforming integer value](https://www.rfc-editor.org/rfc/rfc7493.html#section-2)
68/// guaranteed to be non-negative. Values are \[0, (2<sup>53</sup>)-1].
69///
70/// All values in a JSONPath query are limited to the \[-2<sup>53</sup>+1, (2<sup>53</sup>)-1]
71/// range for interoperability
72/// (see [RFC 2.1-4.1](https://www.ietf.org/archive/id/draft-ietf-jsonpath-base-21.html#section-2.1-4.1)).
73/// Some, like array indices, are additionally restricted to the non-negative part.
74///
75/// The signed version is [`JsonInt`].
76///
77/// # Examples
78/// ```
79/// # use rsonpath_syntax::num::JsonUInt;
80/// let two = JsonUInt::from(2);
81/// let zero = JsonUInt::from(0);
82///
83/// assert_eq!(two.as_u64(), 2);
84/// assert_eq!(zero.as_u64(), 0);
85///
86/// let too_big = JsonUInt::try_from(1_u64 << 53).expect_err("out of range");
87/// ```
88#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
89#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
90pub struct JsonUInt(u64);
91
92/// Unsigned interoperable JSON integer known to be non-zero.
93///
94/// Provides an [IETF-conforming integer value](https://www.rfc-editor.org/rfc/rfc7493.html#section-2)
95/// guaranteed to be positive. Values are \(0, (2<sup>53</sup>)-1].
96///
97/// All values in a JSONPath query are limited to the \[-2<sup>53</sup>+1, (2<sup>53</sup>)-1]
98/// range for interoperability
99/// (see [RFC 2.1-4.1](https://www.ietf.org/archive/id/draft-ietf-jsonpath-base-21.html#section-2.1-4.1)).
100/// Some, like array indices, are additionally restricted to the non-negative part, while
101/// indexing from the end of an array requires a positive value.
102///
103/// The zero-compatible version is [`JsonUInt`].
104///
105/// # Examples
106/// ```
107/// # use rsonpath_syntax::num::JsonNonZeroUInt;
108/// let two = JsonNonZeroUInt::try_from(2).expect("within range");
109/// assert_eq!(two.as_u64(), 2);
110///
111/// let zero = JsonNonZeroUInt::try_from(0).expect_err("out of range");
112/// let too_big = JsonNonZeroUInt::try_from(1_u64 << 53).expect_err("out of range");
113/// ```
114#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
115#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
116pub struct JsonNonZeroUInt(NonZeroU64);
117
118/// IEEE 754 conformant floating-point number expressible in JSON.
119///
120/// These numbers behave as standard binary64 (double precision) numbers
121/// restricted as in [the JSON specification](https://www.rfc-editor.org/rfc/rfc7159#section-6),
122/// i.e. they cannot be NaN, +Inf, or -Inf.
123///
124/// These restrictions allow some "nice" properties - [`JsonFloat`] implements
125/// [`Eq`] and [`Ord`], as well as [`Hash`](std::hash::Hash), and its binary representation
126/// is the same as a regular [`f64`].
127///
128/// ## Integer conversions
129///
130/// Because of interoperability restrictions on [`JsonInt`], any [`JsonInt`] losslessly converts
131/// to a [`JsonFloat`] and back. Therefore, [`JsonInt`] is [`Into<JsonFloat>`](`Into`), and
132/// [`JsonFloat`] is [`TryInto<JsonInt>`], where the conversion succeeds if and only if
133/// the float is an exactly representable integer in the range \[-2<sup>53</sup>+1, (2<sup>53</sup>)-1].
134#[derive(Clone, Copy, Debug, PartialEq)]
135#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
136pub struct JsonFloat(f64);
137
138// This is correct since the allowed values for `JsonFloat` don't include NaNs or infinities.
139impl Eq for JsonFloat {}
140impl PartialOrd for JsonFloat {
141    #[inline(always)]
142    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
143        Some(self.cmp(other))
144    }
145}
146impl Ord for JsonFloat {
147    #[inline(always)]
148    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
149        self.0.partial_cmp(&other.0).expect("JsonFloat never NaN")
150    }
151}
152
153impl std::hash::Hash for JsonFloat {
154    #[inline(always)]
155    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
156        self.0.to_bits().hash(state);
157    }
158}
159
160/// JSONPath numeric type - either a [`JsonInt`] or a [`JsonFloat`].
161///
162/// Note that this type is not normalized and an integer in the range
163/// \[-2<sup>53</sup>+1, (2<sup>53</sup>)-1] can be represented both as
164/// a [`JsonNumber::Int`] and as a [`JsonNumber::Float`].
165///
166/// Which type is produced when is a parser implementation detail.
167/// If you need to rely on integers always being represented as [`JsonNumber::Int`]
168/// you can use [`JsonNumber::normalize`], or manually inspect the underlying
169/// [`JsonFloat`] using [`JsonFloat::is_int`] and its [`TryInto<JsonInt>`] conversion.
170///
171/// ## Examples
172///
173/// ```
174/// # use rsonpath_syntax::num::{JsonNumber, JsonInt, JsonFloat};
175///
176/// let int = JsonInt::from(42);
177/// let float = JsonFloat::try_from(42.01).unwrap();
178///
179/// let num_int = JsonNumber::from(int);
180/// let num_float = JsonNumber::from(float);
181///
182/// assert_eq!(num_int, JsonNumber::Int(int));
183/// assert_eq!(num_float, JsonNumber::Float(float));
184/// assert_eq!("42", num_int.to_string());
185/// assert_eq!("42.01", num_float.to_string());
186/// ```
187#[derive(Clone, Copy, Debug)]
188#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
189#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
190pub enum JsonNumber {
191    /// A [`JsonInt`] number.
192    Int(JsonInt),
193    /// A [`JsonFloat`] number.
194    Float(JsonFloat),
195}
196
197impl PartialEq for JsonNumber {
198    #[inline]
199    fn eq(&self, other: &Self) -> bool {
200        match (self.normalize(), other.normalize()) {
201            (Self::Int(l0), Self::Int(r0)) => l0 == r0,
202            (Self::Float(l0), Self::Float(r0)) => l0 == r0,
203            _ => false,
204        }
205    }
206}
207
208impl Eq for JsonNumber {}
209
210impl std::hash::Hash for JsonNumber {
211    #[inline]
212    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
213        match self.normalize() {
214            Self::Int(i) => (0, i).hash(state),
215            Self::Float(f) => (1, f).hash(state),
216        }
217    }
218}
219
220impl PartialOrd for JsonNumber {
221    #[inline(always)]
222    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
223        Some(self.cmp(other))
224    }
225}
226
227impl Ord for JsonNumber {
228    #[inline]
229    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
230        match (self, other) {
231            (Self::Int(i1), Self::Int(i2)) => i1.cmp(i2),
232            (Self::Int(i), Self::Float(f)) => JsonFloat::from(*i).cmp(f),
233            (Self::Float(f), Self::Int(i)) => f.cmp(&JsonFloat::from(*i)),
234            (Self::Float(f1), Self::Float(f2)) => f1.cmp(f2),
235        }
236    }
237}
238
239/// The upper unsigned inclusive bound on JSON integers (2<sup>53</sup>-1).
240const JSON_UINT_UPPER_LIMIT: u64 = (1 << 53) - 1;
241/// The upper inclusive bound on JSON integers (2<sup>53</sup>-1).
242const JSON_INT_UPPER_LIMIT: i64 = (1 << 53) - 1;
243/// The lower inclusive bound on JSON integers (-2<sup>53</sup>+1).
244const JSON_INT_LOWER_LIMIT: i64 = -(1 << 53) + 1;
245
246impl JsonInt {
247    /// A constant value of zero. Equivalent to [`JsonInt::default`](`Default::default`).
248    ///
249    /// # Examples
250    /// ```
251    /// # use rsonpath_syntax::num::JsonInt;
252    /// assert_eq!(JsonInt::ZERO.as_i64(), 0);
253    /// ```
254    pub const ZERO: Self = Self::new(0);
255
256    /// A constant value of one.
257    ///
258    /// # Examples
259    /// ```
260    /// # use rsonpath_syntax::num::JsonInt;
261    /// assert_eq!(JsonInt::ONE.as_i64(), 1);
262    /// ```
263    pub const ONE: Self = Self::new(1);
264
265    /// A constant for the smallest expressible value.
266    ///
267    /// # Examples
268    /// ```
269    /// # use rsonpath_syntax::num::JsonInt;
270    /// let min_i64 = -(1 << 53) + 1;
271    ///
272    /// assert_eq!(JsonInt::MIN.as_i64(), min_i64);
273    /// assert_eq!(JsonInt::try_from(min_i64).expect("within range"), JsonInt::MIN);
274    /// ```
275    pub const MIN: Self = Self::new(JSON_INT_LOWER_LIMIT);
276
277    /// A constant for the largest expressible value.
278    ///
279    /// # Examples
280    /// ```
281    /// # use rsonpath_syntax::num::JsonInt;
282    /// let max_i64 = (1 << 53) - 1;
283    ///
284    /// assert_eq!(JsonInt::MAX.as_i64(), max_i64);
285    /// assert_eq!(JsonInt::try_from(max_i64).expect("within range"), JsonInt::MAX);
286    /// ```
287    pub const MAX: Self = Self::new(JSON_INT_UPPER_LIMIT);
288
289    /// Create a new value from a [`i64`].
290    #[must_use]
291    const fn new(index: i64) -> Self {
292        Self(index)
293    }
294
295    /// Increase the integer by one.
296    ///
297    /// # Errors
298    /// Will return `Err` if the increment causes the [`JsonInt`] to exceed
299    /// the upper limit of [`JsonInt::MAX`].
300    ///
301    /// # Examples
302    /// ```
303    /// # use rsonpath_syntax::num::JsonInt;
304    /// let mut x = JsonInt::ZERO;
305    /// x.try_increment().expect("within range");
306    /// assert_eq!(x.as_i64(), 1);
307    ///
308    /// let mut y = JsonInt::MIN;
309    /// y.try_increment().expect("within range");
310    /// assert_eq!(y.as_i64(), -(1 << 53) + 2);
311    ///
312    /// JsonInt::MAX.try_increment().expect_err("out of range");
313    /// ```
314    #[inline]
315    pub fn try_increment(&mut self) -> Result<(), JsonIntOverflowError> {
316        let new_index = self.0 + 1;
317        if new_index <= JSON_INT_UPPER_LIMIT {
318            self.0 = new_index;
319            Ok(())
320        } else {
321            Err(JsonIntOverflowError::int_neg_overflow(new_index))
322        }
323    }
324
325    /// Return the value stored as a regular [`i64`].
326    ///
327    /// # Examples
328    /// ```
329    /// # use rsonpath_syntax::num::JsonInt;
330    /// let val = JsonInt::from(42);
331    /// assert_eq!(val.as_i64(), 42);
332    /// ```
333    #[must_use]
334    #[inline(always)]
335    pub const fn as_i64(&self) -> i64 {
336        self.0
337    }
338
339    /// Return the negation of the value.
340    ///
341    /// This is guaranteed to succeed, as the valid range is symmetrical.
342    /// ```
343    /// # use rsonpath_syntax::num::JsonInt;
344    /// let x = JsonInt::from(-42);
345    /// assert_eq!(x.neg().as_i64(), 42);
346    /// ```
347    #[must_use]
348    #[inline(always)]
349    pub const fn neg(&self) -> Self {
350        Self(-self.0)
351    }
352
353    /// Return the absolute value of this integer as a [`JsonUInt`].
354    ///
355    /// This is guaranteed to succeed, as the valid range is symmetrical.
356    ///
357    /// # Examples
358    /// ```
359    /// # use rsonpath_syntax::num::{JsonInt, JsonUInt};
360    /// let pos = JsonInt::from(42);
361    /// let neg = JsonInt::from(-42);
362    /// assert_eq!(neg.abs().as_u64(), 42);
363    /// assert_eq!(pos.abs().as_u64(), 42);
364    /// ```
365    #[inline(always)]
366    #[must_use]
367    pub const fn abs(&self) -> JsonUInt {
368        JsonUInt(self.0.unsigned_abs())
369    }
370}
371
372impl JsonUInt {
373    /// A constant value of zero. Equivalent to [`JsonUInt::default`](`Default::default`).
374    ///
375    /// # Examples
376    /// ```
377    /// # use rsonpath_syntax::num::JsonUInt;
378    /// assert_eq!(JsonUInt::ZERO.as_u64(), 0);
379    /// ```
380    pub const ZERO: Self = Self::new(0);
381
382    /// A constant value of one.
383    ///
384    /// # Examples
385    /// ```
386    /// # use rsonpath_syntax::num::JsonUInt;
387    /// assert_eq!(JsonUInt::ONE.as_u64(), 1);
388    /// ```
389    pub const ONE: Self = Self::new(1);
390
391    /// A constant for the largest expressible value.
392    ///
393    /// # Examples
394    /// ```
395    /// # use rsonpath_syntax::num::JsonUInt;
396    /// let max_u64 = (1 << 53) - 1;
397    ///
398    /// assert_eq!(JsonUInt::MAX.as_u64(), max_u64);
399    /// assert_eq!(JsonUInt::try_from(max_u64).expect("within range"), JsonUInt::MAX);
400    /// ```
401    pub const MAX: Self = Self::new(JSON_UINT_UPPER_LIMIT);
402
403    /// Create a new value from a [`u64`].
404    #[must_use]
405    const fn new(index: u64) -> Self {
406        Self(index)
407    }
408
409    /// Increase the integer by one.
410    ///
411    /// # Errors
412    /// Will return `Err` if the increment causes the [`JsonUInt`] to exceed
413    /// the upper limit of [`JsonUInt::MAX`].
414    ///
415    /// # Examples
416    /// ```
417    /// # use rsonpath_syntax::num::JsonUInt;
418    /// let mut x = JsonUInt::ZERO;
419    /// x.try_increment().expect("within range");
420    /// JsonUInt::MAX.try_increment().expect_err("out of range");
421    ///
422    /// assert_eq!(x.as_u64(), 1);
423    /// ```
424    #[inline]
425    pub fn try_increment(&mut self) -> Result<(), JsonIntOverflowError> {
426        let new_index = self.0 + 1;
427        if new_index <= JSON_UINT_UPPER_LIMIT {
428            self.0 = new_index;
429            Ok(())
430        } else {
431            Err(JsonIntOverflowError::uint_pos_overflow(new_index))
432        }
433    }
434
435    /// Return the negation of the value as a [`JsonInt`].
436    ///
437    /// This is guaranteed to succeed, as the valid range is symmetrical.
438    /// ```
439    /// # use rsonpath_syntax::num::{JsonInt, JsonUInt};
440    /// let x = JsonUInt::from(42);
441    /// let y = JsonInt::from(-42);
442    /// assert_eq!(x.neg(), y);
443    /// ```
444    #[must_use]
445    #[inline(always)]
446    pub const fn neg(&self) -> JsonInt {
447        JsonInt(-(self.0 as i64))
448    }
449
450    /// Return the value stored as a regular [`u64`].
451    ///
452    /// # Examples
453    /// ```
454    /// # use rsonpath_syntax::num::JsonUInt;
455    /// let val = JsonUInt::from(42);
456    /// assert_eq!(val.as_u64(), 42);
457    /// ```
458    #[must_use]
459    #[inline(always)]
460    pub const fn as_u64(&self) -> u64 {
461        self.0
462    }
463}
464
465impl JsonNonZeroUInt {
466    #[must_use]
467    const fn new(value: NonZeroU64) -> Self {
468        Self(value)
469    }
470
471    /// Return the value stored as a [`NonZeroU64`].
472    ///
473    /// # Examples
474    /// ```
475    /// # use rsonpath_syntax::num::JsonNonZeroUInt;
476    /// # use std::num::NonZeroU64;
477    /// let val = JsonNonZeroUInt::try_from(42).unwrap();
478    /// assert_eq!(val.as_non_zero_u64(), NonZeroU64::new(42).unwrap());
479    /// ```
480    #[must_use]
481    #[inline(always)]
482    pub const fn as_non_zero_u64(&self) -> NonZeroU64 {
483        self.0
484    }
485
486    /// Return the value stored as a [`u64`].
487    ///
488    /// # Examples
489    /// ```
490    /// # use rsonpath_syntax::num::JsonNonZeroUInt;
491    /// let val = JsonNonZeroUInt::try_from(42).unwrap();
492    /// assert_eq!(val.as_u64(), 42);
493    /// ```
494    #[must_use]
495    #[inline(always)]
496    pub const fn as_u64(&self) -> u64 {
497        self.0.get()
498    }
499}
500
501impl JsonFloat {
502    fn new(x: f64) -> Self {
503        debug_assert!(x.is_finite());
504        Self(x)
505    }
506
507    /// Return the value stored as a [`f64`].
508    ///
509    /// # Examples
510    /// ```
511    /// # use rsonpath_syntax::num::JsonFloat;
512    /// let val = JsonFloat::try_from(3.14).unwrap();
513    /// assert_eq!(val.as_f64(), 3.14);
514    /// ```
515    #[inline]
516    #[must_use]
517    pub fn as_f64(&self) -> f64 {
518        self.0
519    }
520
521    /// Check if this float is an equivalent of some [`JsonInt`].
522    ///
523    /// The range of valid [`JsonInt`] is exactly representable as [`JsonFloat`] values.
524    /// This function returns true if the float is one of those valid values, i.e. an
525    /// integer and in the [`JsonInt`] bounds.
526    ///
527    /// ## Examples
528    /// ```
529    /// # use rsonpath_syntax::num::JsonFloat;
530    ///
531    /// let f1 = JsonFloat::try_from(3.0).unwrap();
532    /// let f2 = JsonFloat::try_from(3.14).unwrap();
533    /// let f3 = JsonFloat::try_from(1e54).unwrap();
534    ///
535    /// assert!(f1.is_int());
536    /// assert!(!f2.is_int());
537    /// assert!(!f3.is_int());
538    /// ```
539    #[inline]
540    #[must_use]
541    pub fn is_int(&self) -> bool {
542        JsonInt::try_from(*self).is_ok()
543    }
544}
545
546impl JsonNumber {
547    /// Normalize a [`JsonNumber`] so that valid [`JsonInt`] value is represented
548    /// by [`JsonNumber::Int`].
549    ///
550    /// The parser is allowed to represent a normal JSON integer (e.g. 17) as an
551    /// equivalent JSON float (17.0). Calling `normalize` ensures all values
552    /// representable by a [`JsonInt`] are indeed represented as such.
553    ///
554    /// ## Examples
555    ///
556    /// ```
557    /// # use rsonpath_syntax::num::{JsonNumber, JsonInt, JsonFloat};
558    ///
559    /// // Creating a JsonNumber from a JsonFloat always gives JsonNumber::Float.
560    /// let f1 = JsonFloat::try_from(17.0).unwrap();
561    /// let nf1 = JsonNumber::from(f1);
562    /// assert_eq!(nf1, JsonNumber::Float(f1));
563    /// // Normalizing will give us an integer representation, when possible.
564    /// assert_eq!(nf1.normalize(), JsonNumber::Int(17.into()));
565    ///
566    /// // If the float is an integer within range normalization will succeed.
567    /// let f2 = JsonFloat::try_from(1e15).unwrap();
568    /// let nf2 = JsonNumber::from(f2);
569    /// assert_eq!(nf2, JsonNumber::Float(f2));
570    /// assert_eq!(nf2.normalize(), JsonNumber::Int(1_000_000_000_000_000_i64.try_into().unwrap()));
571    ///
572    /// // An int is an int, and remains so under normalization.
573    /// let i1 = JsonInt::from(42);
574    /// let ni1 = JsonNumber::from(i1);
575    /// assert_eq!(ni1, JsonNumber::Int(i1));
576    /// assert_eq!(ni1.normalize(), JsonNumber::Int(i1));
577    ///
578    /// // A float that is not an int remains the same when normalized.
579    /// let f3 = JsonFloat::try_from(3.14).unwrap();
580    /// let nf3 = JsonNumber::from(f3);
581    /// assert_eq!(nf3, JsonNumber::Float(f3));
582    /// assert_eq!(nf3.normalize(), JsonNumber::Float(f3));
583    ///
584    /// // A float that is an int, but outside of the interoperable JsonInt range,
585    /// // is not normalized.
586    /// let f4 = JsonFloat::try_from(1e120).unwrap();
587    /// let nf4 = JsonNumber::from(f4);
588    /// assert_eq!(nf4, JsonNumber::Float(f4));
589    /// assert_eq!(nf4.normalize(), JsonNumber::Float(f4));
590    /// ```
591    #[inline]
592    #[must_use]
593    pub fn normalize(&self) -> Self {
594        match *self {
595            Self::Int(x) => Self::Int(x),
596            Self::Float(x) => match JsonInt::try_from(x) {
597                Ok(int) => Self::Int(int),
598                Err(_) => Self::Float(x),
599            },
600        }
601    }
602}
603
604impl TryFrom<i64> for JsonInt {
605    type Error = JsonIntOverflowError;
606
607    #[inline]
608    fn try_from(value: i64) -> Result<Self, Self::Error> {
609        if value > JSON_INT_UPPER_LIMIT {
610            Err(JsonIntOverflowError::int_pos_overflow(value))
611        } else if value < JSON_INT_LOWER_LIMIT {
612            Err(JsonIntOverflowError::int_neg_overflow(value))
613        } else {
614            Ok(Self::new(value))
615        }
616    }
617}
618
619impl TryFrom<u64> for JsonInt {
620    type Error = JsonIntOverflowError;
621
622    #[inline]
623    fn try_from(value: u64) -> Result<Self, Self::Error> {
624        if value > i64::MAX as u64 {
625            Err(JsonIntOverflowError::int_pos_overflow_u(value))
626        } else {
627            Self::try_from(value as i64)
628        }
629    }
630}
631
632impl From<i32> for JsonInt {
633    // i32 is always in the range (-2^53, 2^53)
634    #[inline]
635    fn from(value: i32) -> Self {
636        Self::new(i64::from(value))
637    }
638}
639
640impl From<u32> for JsonInt {
641    // u32 is always in the range (-2^53, 2^53)
642    #[inline]
643    fn from(value: u32) -> Self {
644        Self::new(i64::from(value))
645    }
646}
647
648impl From<JsonInt> for i64 {
649    #[inline(always)]
650    fn from(value: JsonInt) -> Self {
651        value.0
652    }
653}
654
655impl From<JsonUInt> for JsonInt {
656    #[inline(always)]
657    fn from(value: JsonUInt) -> Self {
658        // This is always safe due to the type invariant bounds.
659        Self::new(value.0 as i64)
660    }
661}
662
663impl FromStr for JsonInt {
664    type Err = JsonIntParseError;
665
666    #[inline]
667    fn from_str(s: &str) -> Result<Self, Self::Err> {
668        match i64::from_str(s) {
669            Ok(x) => x.try_into().map_err(|e| Self::Err::parse_conversion_err(s, &e)),
670            Err(err) => Err(Self::Err::int_parse_error(s, err.kind())),
671        }
672    }
673}
674
675impl TryFrom<u64> for JsonUInt {
676    type Error = JsonIntOverflowError;
677
678    #[inline]
679    fn try_from(value: u64) -> Result<Self, Self::Error> {
680        if value > JSON_UINT_UPPER_LIMIT {
681            Err(JsonIntOverflowError::uint_pos_overflow(value))
682        } else {
683            Ok(Self::new(value))
684        }
685    }
686}
687
688impl TryFrom<i64> for JsonUInt {
689    type Error = JsonIntOverflowError;
690
691    #[inline]
692    fn try_from(value: i64) -> Result<Self, Self::Error> {
693        if value < 0 {
694            Err(JsonIntOverflowError::negative_uint(value))
695        } else {
696            Self::try_from(value as u64)
697        }
698    }
699}
700
701impl From<u32> for JsonUInt {
702    // u32 is always in the range [0, 2^53)
703    #[inline]
704    fn from(value: u32) -> Self {
705        Self::new(u64::from(value))
706    }
707}
708
709impl TryFrom<i32> for JsonUInt {
710    type Error = JsonIntOverflowError;
711
712    #[inline]
713    fn try_from(value: i32) -> Result<Self, Self::Error> {
714        if value < 0 {
715            Err(JsonIntOverflowError::negative_uint(i64::from(value)))
716        } else {
717            Ok(Self::from(value as u32))
718        }
719    }
720}
721
722impl From<JsonUInt> for u64 {
723    #[inline(always)]
724    fn from(value: JsonUInt) -> Self {
725        value.0
726    }
727}
728
729impl From<JsonUInt> for i64 {
730    #[inline(always)]
731    fn from(value: JsonUInt) -> Self {
732        // Safe cast since JsonUInt::MAX is lower than i64::MAX.
733        value.0 as Self
734    }
735}
736
737impl TryFrom<JsonInt> for JsonUInt {
738    type Error = JsonIntOverflowError;
739
740    #[inline]
741    fn try_from(value: JsonInt) -> Result<Self, Self::Error> {
742        if value.0 < 0 {
743            Err(JsonIntOverflowError::negative_uint(value.0))
744        } else {
745            Ok(Self::new(value.0 as u64))
746        }
747    }
748}
749
750impl FromStr for JsonUInt {
751    type Err = JsonIntParseError;
752
753    #[inline]
754    fn from_str(s: &str) -> Result<Self, Self::Err> {
755        match i64::from_str(s) {
756            // u64 would work but i64 gives us a better error message for negative values.
757            Ok(x) => x.try_into().map_err(|e| Self::Err::parse_conversion_err(s, &e)),
758            Err(err) => Err(Self::Err::uint_parse_error(s, err.kind())),
759        }
760    }
761}
762
763impl From<NonZeroU32> for JsonNonZeroUInt {
764    // NonZeroU32 is always in the range (0, 2^53)
765    #[inline]
766    fn from(value: NonZeroU32) -> Self {
767        Self::new(NonZeroU64::from(value))
768    }
769}
770
771impl From<NonZeroU64> for JsonNonZeroUInt {
772    // NonZeroU64 is always in the range (0, 2^53)
773    #[inline]
774    fn from(value: NonZeroU64) -> Self {
775        Self::new(value)
776    }
777}
778
779impl TryFrom<u32> for JsonNonZeroUInt {
780    type Error = JsonIntOverflowError;
781
782    #[inline]
783    fn try_from(value: u32) -> Result<Self, Self::Error> {
784        Self::try_from(u64::from(value))
785    }
786}
787
788impl TryFrom<i32> for JsonNonZeroUInt {
789    type Error = JsonIntOverflowError;
790
791    #[inline]
792    fn try_from(value: i32) -> Result<Self, Self::Error> {
793        Self::try_from(i64::from(value))
794    }
795}
796
797impl TryFrom<u64> for JsonNonZeroUInt {
798    type Error = JsonIntOverflowError;
799
800    #[inline]
801    fn try_from(value: u64) -> Result<Self, Self::Error> {
802        if value > JSON_UINT_UPPER_LIMIT {
803            Err(JsonIntOverflowError::uint_pos_overflow(value))
804        } else if let Some(x) = NonZeroU64::new(value) {
805            Ok(Self(x))
806        } else {
807            Err(JsonIntOverflowError::zero_non_zero_uint())
808        }
809    }
810}
811
812impl TryFrom<i64> for JsonNonZeroUInt {
813    type Error = JsonIntOverflowError;
814
815    #[inline]
816    fn try_from(value: i64) -> Result<Self, Self::Error> {
817        if value < 0 {
818            Err(JsonIntOverflowError::negative_uint(value))
819        } else {
820            Self::try_from(value as u64)
821        }
822    }
823}
824
825impl TryFrom<JsonUInt> for JsonNonZeroUInt {
826    type Error = JsonIntOverflowError;
827
828    #[inline]
829    fn try_from(value: JsonUInt) -> Result<Self, Self::Error> {
830        Self::try_from(value.0)
831    }
832}
833
834impl From<JsonNonZeroUInt> for JsonUInt {
835    #[inline]
836    fn from(value: JsonNonZeroUInt) -> Self {
837        Self::new(value.0.get())
838    }
839}
840
841impl FromStr for JsonNonZeroUInt {
842    type Err = JsonIntParseError;
843
844    #[inline]
845    fn from_str(s: &str) -> Result<Self, Self::Err> {
846        match i64::from_str(s) {
847            // u64 would work but i64 gives us a better error message for negative values.
848            Ok(x) => x.try_into().map_err(|e| Self::Err::parse_conversion_err(s, &e)),
849            Err(err) => Err(Self::Err::non_zero_uint_parse_error(s, err.kind())),
850        }
851    }
852}
853
854impl TryFrom<JsonFloat> for JsonInt {
855    type Error = JsonIntOverflowError;
856
857    #[inline]
858    fn try_from(value: JsonFloat) -> Result<Self, Self::Error> {
859        if value.0.fract() != 0.0 {
860            return Err(JsonIntOverflowError::fractional(value.0));
861        }
862        // At this point the fractional part must be 0.0, so the value is *an* integer.
863        // We need to check that it is within bounds of JsonInt. This is correct
864        // only because JsonInt bounds are guaranteed to be interoperable with f64,
865        // so every value within is exactly representable as a f64.
866        let int_value = value.0.trunc();
867        if int_value < JSON_INT_LOWER_LIMIT as f64 {
868            return Err(JsonIntOverflowError::int_float_neg_overflow(value.0));
869        }
870        if int_value > JSON_INT_UPPER_LIMIT as f64 {
871            return Err(JsonIntOverflowError::int_float_pos_overflow(value.0));
872        }
873
874        // This conversion is now guaranteed to be lossless.
875        Ok(Self(int_value as i64))
876    }
877}
878
879impl From<JsonInt> for JsonFloat {
880    #[inline]
881    fn from(value: JsonInt) -> Self {
882        Self::new(value.0 as f64)
883    }
884}
885
886impl TryFrom<f32> for JsonFloat {
887    type Error = JsonFloatConvertError;
888
889    #[inline]
890    fn try_from(value: f32) -> Result<Self, Self::Error> {
891        if value.is_finite() {
892            Ok(Self::new(f64::from(value)))
893        } else {
894            Err(JsonFloatConvertError::infinite_or_nan(f64::from(value)))
895        }
896    }
897}
898
899impl TryFrom<f64> for JsonFloat {
900    type Error = JsonFloatConvertError;
901
902    #[inline]
903    fn try_from(value: f64) -> Result<Self, Self::Error> {
904        if value.is_finite() {
905            Ok(Self::new(value))
906        } else {
907            Err(JsonFloatConvertError::infinite_or_nan(value))
908        }
909    }
910}
911
912impl FromStr for JsonFloat {
913    type Err = JsonFloatParseError;
914
915    /* Fact #1: parsing floats is hard.
916    * Fact #2: grammar accepted by `f64::from_str` is slightly different from the
917    *          JSONPath grammar for floats.
918    * Fact #3: I have little interest in rewriting the close to 2,000 lines of code
919    *          of Rust's `dec2flt` to incorporate those differences.
920    *
921    * The grammars accepted by Rust and JSON are, respectively:
922      ; Rust f64::from_str
923      Float  ::= Sign? ( 'inf' | 'infinity' | 'nan' | Number )
924      Number ::= ( Digit+ |
925                   Digit+ '.' Digit* |
926                   Digit* '.' Digit+ ) Exp?
927      Exp    ::= 'e' Sign? Digit+
928      Sign   ::= [+-]
929      Digit  ::= [0-9]
930
931      ; JSON
932      Number ::= (Int | "-0") Frac? Exp?
933      Int    ::= "0" |
934                 ("-"? Digit1 Digit*)
935      Frac   ::= "." Digit+
936      Exp    ::= "e" Sign? Digit+
937      Sign   ::= [+-]
938      Digit  ::= [0-9]
939      Digit1 ::= [1-9]
940
941    * Here are all the differences:
942    * 1) 'inf', 'infinity', and 'nan' are acceptable only in Rust.
943    * 2) Rust allows an explicit leading `+`, JSON does not.
944    * 3) Rust allows an empty integer part, e.g. '.14' as equivalent to '0.14'; JSON does not.
945    * 4) Rust allows an empty decimal part, e.g. '3.' as equivalent to '3.0'; JSON does not.
946    * 5) Leading zeroes of the integral part are accepted only in Rust.
947    *
948    * Both accept the exponent char as either lower or uppercase.
949    * Since Rust's grammar is more general than JSON (L(JSON) \subset L(Rust))
950    * we can use Rust's parser and enforce stricter JSON rules independently.
951    *
952    * To satisfy all restrictions, we parse with Rust first and then:
953    * - enforce the result is not Inf or NaN (rule 1);
954    * - enforce the string does not begin with '+' (rule 2);
955    * - check if the decimal period exists (Rust guarantees there is at most one),
956    *   and enforce it is both preceded and followed by a digit (rules 3 and 4);
957    * - enforce there are no leading zeroes (rule 5).
958    *
959    * Performance-wise this is not ideal - we're effectively inspecting the string twice.
960    * But without access into the `f64::from_str` black-box the only solution would be
961    * to rewrite the routine here and add the restrictions, and we rejected that at the start.
962    * If Rust ever exposes an API to create an f64 out of the mantissa and exponent then it might
963    * be possible - the hardest bits of the parsing routine happen after these are actually extracted
964    * from the string. See: https://github.com/rust-lang/rust/blob/master/library/core/src/num/dec2flt/mod.rs
965    */
966
967    #[inline]
968    fn from_str(s: &str) -> Result<Self, Self::Err> {
969        match f64::from_str(s) {
970            Ok(x) => {
971                assert!(!s.is_empty()); // Empty strings are not accepted by f64::from_str.
972                                        // Rule 1.
973                if x.is_nan() || x.is_infinite() {
974                    return Err(Self::Err::infinite_or_nan(s));
975                }
976                if let Some((before, after)) = s.split_once('.') {
977                    // Rule 3. The case `before == "+"` is checked later.
978                    if before.is_empty() || before == "-" {
979                        return Err(Self::Err::nothing_before_decimal_point(s));
980                    }
981                    // Rule 4.
982                    if after.is_empty() || after.starts_with(['e', 'E']) {
983                        return Err(Self::Err::nothing_after_decimal_point(s));
984                    }
985                }
986                let mut chars = s.chars();
987                let first_c = chars.next().expect("s is non-empty");
988                // Rule 2.
989                if first_c == '+' {
990                    return Err(Self::Err::leading_plus_sign(s));
991                }
992                // Skip the leading minus if it exists.
993                let s_no_sign = if first_c == '-' { chars.as_str() } else { s };
994                // Rule 5.
995                // Check for leading zeroes. We strip the first zero from the front and check what's left.
996                // The only acceptable case is that the next character is not a digit.
997                if let Some(rest) = s_no_sign.strip_prefix('0') {
998                    if matches!(rest.chars().next(), Some('0'..='9')) {
999                        return Err(Self::Err::leading_zeros(s));
1000                    }
1001                }
1002                Ok(Self(x))
1003            }
1004            // Remember that all floats valid in JSON are also accepted by Rust,
1005            // so this is *definitely* not a valid JSON float.
1006            Err(_) => Err(Self::Err::f64_parse_error(s)),
1007        }
1008    }
1009}
1010
1011impl From<JsonInt> for JsonNumber {
1012    #[inline]
1013    fn from(value: JsonInt) -> Self {
1014        Self::Int(value)
1015    }
1016}
1017
1018impl From<JsonFloat> for JsonNumber {
1019    #[inline]
1020    fn from(value: JsonFloat) -> Self {
1021        Self::Float(value)
1022    }
1023}
1024
1025impl Display for JsonInt {
1026    #[inline]
1027    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1028        write!(f, "{}", self.0)
1029    }
1030}
1031
1032impl Display for JsonUInt {
1033    #[inline]
1034    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1035        write!(f, "{}", self.0)
1036    }
1037}
1038
1039impl Display for JsonNonZeroUInt {
1040    #[inline]
1041    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1042        write!(f, "{}", self.0)
1043    }
1044}
1045
1046impl Display for JsonFloat {
1047    #[inline]
1048    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1049        write!(f, "{}", self.0)
1050    }
1051}
1052
1053impl Display for JsonNumber {
1054    #[inline]
1055    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1056        match self {
1057            Self::Int(int) => int.fmt(f),
1058            Self::Float(flt) => flt.fmt(f),
1059        }
1060    }
1061}
1062
1063#[cfg(feature = "arbitrary")]
1064#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
1065impl<'a> arbitrary::Arbitrary<'a> for JsonInt {
1066    #[inline]
1067    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
1068        let val = u.int_in_range(JSON_INT_LOWER_LIMIT..=JSON_INT_UPPER_LIMIT)?;
1069
1070        Ok(Self::new(val))
1071    }
1072}
1073
1074#[cfg(feature = "arbitrary")]
1075#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
1076impl<'a> arbitrary::Arbitrary<'a> for JsonUInt {
1077    #[inline]
1078    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
1079        let val = u.int_in_range(0..=JSON_UINT_UPPER_LIMIT)?;
1080
1081        Ok(Self::new(val))
1082    }
1083}
1084
1085#[cfg(feature = "arbitrary")]
1086#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
1087impl<'a> arbitrary::Arbitrary<'a> for JsonNonZeroUInt {
1088    #[inline]
1089    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
1090        let val = u.int_in_range(1..=JSON_UINT_UPPER_LIMIT)?;
1091
1092        Ok(Self::new(NonZeroU64::new(val).expect("range starts at 1")))
1093    }
1094}
1095
1096#[cfg(feature = "arbitrary")]
1097#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
1098impl<'a> arbitrary::Arbitrary<'a> for JsonFloat {
1099    #[inline]
1100    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
1101        let val = u.arbitrary::<f64>()?;
1102        // Wrap NaN, +Inf, -Inf into zero.
1103        let val = if val.is_nan() {
1104            0.0
1105        } else if val.is_infinite() {
1106            (0.0_f64).copysign(val)
1107        } else {
1108            val
1109        };
1110
1111        Ok(Self(val))
1112    }
1113}
1114
1115#[cfg(test)]
1116mod tests {
1117    use super::*;
1118    use pretty_assertions::assert_eq;
1119
1120    #[test]
1121    fn int_upper_limit_sanity_check() {
1122        assert_eq!(JSON_INT_UPPER_LIMIT, (1 << 53) - 1);
1123        assert_eq!(JSON_INT_UPPER_LIMIT, 9_007_199_254_740_991);
1124    }
1125
1126    #[test]
1127    fn int_lower_limit_sanity_check() {
1128        assert_eq!(JSON_INT_LOWER_LIMIT, -(1 << 53) + 1);
1129        assert_eq!(JSON_INT_LOWER_LIMIT, -9_007_199_254_740_991);
1130        assert_eq!(JSON_INT_LOWER_LIMIT, -JSON_INT_UPPER_LIMIT);
1131    }
1132
1133    #[test]
1134    fn uint_upper_limit_sanity_check() {
1135        assert_eq!(JSON_UINT_UPPER_LIMIT, (1 << 53) - 1);
1136        assert_eq!(JSON_UINT_UPPER_LIMIT, 9_007_199_254_740_991);
1137        assert_eq!(JSON_INT_UPPER_LIMIT, JSON_UINT_UPPER_LIMIT as i64);
1138    }
1139
1140    #[test]
1141    fn int_lower_limit_try_from_check() {
1142        let min = JsonInt::try_from(JSON_INT_LOWER_LIMIT).expect("JSON int lower_limit should be convertible.");
1143        let err = JsonInt::try_from(JSON_INT_LOWER_LIMIT - 1)
1144            .expect_err("Values below JSON int lower_limit should not be convertible.");
1145        assert_eq!(min.as_i64(), JSON_INT_LOWER_LIMIT);
1146        assert_eq!(
1147            err.to_string(),
1148            "value -9007199254740992 is below the range of JsonInt values [-9007199254740991..9007199254740991]"
1149        );
1150    }
1151
1152    #[test]
1153    fn int_upper_limit_try_from_check() {
1154        let max = JsonInt::try_from(JSON_INT_UPPER_LIMIT).expect("JSON int upper_limit should be convertible.");
1155        let err = JsonInt::try_from(JSON_INT_UPPER_LIMIT + 1)
1156            .expect_err("Values in excess of JSON int upper_limit should not be convertible.");
1157        assert_eq!(max.as_i64(), JSON_INT_UPPER_LIMIT);
1158        assert_eq!(
1159            err.to_string(),
1160            "value 9007199254740992 is above the range of JsonInt values [-9007199254740991..9007199254740991]"
1161        );
1162    }
1163
1164    #[test]
1165    fn uint_upper_limit_try_from_check() {
1166        let max = JsonUInt::try_from(JSON_UINT_UPPER_LIMIT).expect("JSON uint upper_limit should be convertible.");
1167        let err = JsonUInt::try_from(JSON_UINT_UPPER_LIMIT + 1)
1168            .expect_err("Values in excess of JSON uint upper_limit should not be convertible.");
1169        assert_eq!(max.as_u64(), JSON_UINT_UPPER_LIMIT);
1170        assert_eq!(
1171            err.to_string(),
1172            "value 9007199254740992 is above the range of JsonUInt values [0..9007199254740991]"
1173        );
1174    }
1175
1176    #[test]
1177    fn non_zero_uint_try_from_zero_check() {
1178        let err_i32 = JsonNonZeroUInt::try_from(0_i32).expect_err("zero should not be convertible");
1179        let err_u32 = JsonNonZeroUInt::try_from(0_u32).expect_err("zero should not be convertible");
1180        let err_i64 = JsonNonZeroUInt::try_from(0_i64).expect_err("zero should not be convertible");
1181        let err_u64 = JsonNonZeroUInt::try_from(0_u64).expect_err("zero should not be convertible");
1182        assert_eq!(
1183            err_i32.to_string(),
1184            "attempt to convert a zero value into a JsonNonZeroUInt"
1185        );
1186        assert_eq!(
1187            err_u32.to_string(),
1188            "attempt to convert a zero value into a JsonNonZeroUInt"
1189        );
1190        assert_eq!(
1191            err_i64.to_string(),
1192            "attempt to convert a zero value into a JsonNonZeroUInt"
1193        );
1194        assert_eq!(
1195            err_u64.to_string(),
1196            "attempt to convert a zero value into a JsonNonZeroUInt"
1197        );
1198    }
1199
1200    #[test]
1201    fn parse_int_from_empty() {
1202        let err = JsonInt::from_str("").expect_err("empty string is not valid");
1203        assert_eq!(
1204            err.to_string(),
1205            "string '' is not a valid representation of a JSON integer"
1206        );
1207    }
1208
1209    #[test]
1210    fn parse_int_underflow() {
1211        let err = JsonInt::from_str("-9007199254740992").expect_err("out of range");
1212        assert_eq!(
1213            err.to_string(),
1214            "string '-9007199254740992' represents a value below the range of JsonInt values [-9007199254740991..9007199254740991]"
1215        );
1216    }
1217
1218    #[test]
1219    fn parse_int_overflow() {
1220        let err = JsonInt::from_str("9007199254740992").expect_err("out of range");
1221        assert_eq!(
1222            err.to_string(),
1223            "string '9007199254740992' represents a value above the range of JsonInt values [-9007199254740991..9007199254740991]"
1224        );
1225    }
1226
1227    #[test]
1228    fn parse_int_from_invalid_characters() {
1229        let err = JsonInt::from_str("42+7").expect_err("not a valid integer");
1230        assert_eq!(
1231            err.to_string(),
1232            "string '42+7' is not a valid representation of a JSON integer"
1233        );
1234    }
1235
1236    #[test]
1237    fn parse_uint_from_empty() {
1238        let err = JsonUInt::from_str("").expect_err("empty string is not valid");
1239        assert_eq!(
1240            err.to_string(),
1241            "string '' is not a valid representation of a JSON integer"
1242        );
1243    }
1244
1245    #[test]
1246    fn parse_uint_from_negative() {
1247        let err = JsonUInt::from_str("-42").expect_err("out of range");
1248        assert_eq!(
1249            err.to_string(),
1250            "string '-42' represents a value below the range of JsonUInt values [0..9007199254740991]"
1251        );
1252    }
1253
1254    #[test]
1255    fn parse_uint_overflow() {
1256        let err = JsonUInt::from_str("9007199254740992").expect_err("out of range");
1257        assert_eq!(
1258            err.to_string(),
1259            "string '9007199254740992' represents a value above the range of JsonUInt values [0..9007199254740991]"
1260        );
1261    }
1262
1263    #[test]
1264    fn parse_uint_from_invalid_characters() {
1265        let err = JsonUInt::from_str("42+7").expect_err("not a valid integer");
1266        assert_eq!(
1267            err.to_string(),
1268            "string '42+7' is not a valid representation of a JSON integer"
1269        );
1270    }
1271
1272    #[test]
1273    fn parse_non_zero_uint_from_zero() {
1274        let err = JsonNonZeroUInt::from_str("0").expect_err("not a non-zero integer");
1275        assert_eq!(
1276            err.to_string(),
1277            "string '0' represents a zero value, which is not a valid JsonNonZeroUInt"
1278        )
1279    }
1280
1281    #[test]
1282    fn convert_large_float_to_int() {
1283        let float = JsonFloat::try_from(1e15).unwrap();
1284        let int = JsonInt::try_from(float).expect("should succeed");
1285        assert_eq!(int.as_i64(), 1_000_000_000_000_000);
1286    }
1287
1288    mod json_float_parse {
1289        use super::*;
1290        use pretty_assertions::assert_eq;
1291        use test_case::test_case;
1292
1293        #[allow(clippy::approx_constant)] // Detects 3.14 as PI, that's not we want for tests.
1294        #[test_case("0.0", 0.0; "0d0")]
1295        #[test_case("0.0e+000000000000000000000", 0.0; "0d0ep000000000000000000000")]
1296        #[test_case("0.0E+000000000000000000000", 0.0; "0d0Uep000000000000000000000")]
1297        #[test_case("-0.0", -0.0; "m0d0")]
1298        #[test_case("3.14", 3.14; "3d142")]
1299        #[test_case("-3.14", -3.14; "m3d142")]
1300        #[test_case("3.14159265358979323846264338327950288", std::f64::consts::PI; "pi")]
1301        #[test_case("-3.00000000000000000000000000000000000000000000000", -3.0; "m3d00000000000000000000000000000000000000000000000")]
1302        #[test_case("-3.14e53", -3.14e53; "m3d14e53")]
1303        #[test_case("-3.14e+53", -3.14e53; "m3d14ep53")]
1304        #[test_case("-3.14e-53", -3.14e-53; "m3d14em53")]
1305        #[test_case("-3.14e-153", -3.14e-153; "m3d14em153")]
1306        #[test_case("42", 42.0; "42")]
1307        fn valid_float_string(str: &str, expected: f64) {
1308            let float = JsonFloat::from_str(str).expect("should parse");
1309            assert_eq!(float.as_f64(), expected);
1310        }
1311
1312        #[test_case("abc")]
1313        #[test_case("0xFF")]
1314        #[test_case("3,14")]
1315        #[test_case("3.14F-20")]
1316        #[test_case("3.3.3")]
1317        #[test_case(".")]
1318        #[test_case(".e30"; "de30")]
1319        #[test_case("e30")]
1320        fn invalid_float_strings_that_even_rust_rejects(str: &str) {
1321            let err = JsonFloat::from_str(str).expect_err("should not parse");
1322            let expected = format!("string '{str}' is not a valid representation of a float");
1323            assert_eq!(err.to_string(), expected);
1324        }
1325
1326        #[test_case("nan"; "nan lowercase")]
1327        #[test_case("NaN"; "NaN mixed case")]
1328        #[test_case("NAN"; "NAN uppercase")]
1329        #[test_case("-nan"; "minus nan lowercase")]
1330        #[test_case("-NaN"; "minus nan mixed case")]
1331        #[test_case("-NAN"; "minus nan uppercase")]
1332        #[test_case("inf"; "inf")]
1333        #[test_case("Inf"; "inf mixed case")]
1334        #[test_case("INF"; "inf uppercase")]
1335        #[test_case("-inf"; "minus inf")]
1336        #[test_case("-Inf"; "minus inf mixed case")]
1337        #[test_case("-INF"; "minus inf uppercase")]
1338        #[test_case("infinity"; "infinity mixed case")]
1339        #[test_case("Infinity"; "infinity")]
1340        #[test_case("INFINITY"; "infinity uppercase")]
1341        #[test_case("-infinity"; "minus infinity")]
1342        #[test_case("-Infinity"; "minus infinity mixed case")]
1343        #[test_case("-INFINITY"; "minus infinity uppercase")]
1344        fn invalid_float_strings_infinity_or_nan(str: &str) {
1345            let err = JsonFloat::from_str(str).expect_err("should not parse");
1346            let expected = format!("string '{str}' is not a valid JsonFloat as it is not a finite number");
1347            assert_eq!(err.to_string(), expected);
1348        }
1349
1350        #[test_case(".14"; "d14")]
1351        #[test_case("-.14"; "md14")]
1352        #[test_case(".0")]
1353        #[test_case(".14e53")]
1354        #[test_case(".00000e53")]
1355        fn invalid_float_strings_nothing_before_decimal_point(str: &str) {
1356            let err = JsonFloat::from_str(str).expect_err("should not parse");
1357            let expected = format!("missing digits before the decimal point in '{str}'");
1358            assert_eq!(err.to_string(), expected);
1359        }
1360
1361        #[test_case("14."; "14d")]
1362        #[test_case("-14."; "m14d")]
1363        #[test_case("-0.")]
1364        #[test_case("14.e53")]
1365        #[test_case("0.e53")]
1366        fn invalid_float_strings_nothing_after_decimal_point(str: &str) {
1367            let err = JsonFloat::from_str(str).expect_err("should not parse");
1368            let expected = format!("missing digits after the decimal point in '{str}'");
1369            assert_eq!(err.to_string(), expected);
1370        }
1371
1372        #[test_case("+3.14")]
1373        #[test_case("+3.14e53")]
1374        fn invalid_float_strings_leading_plus_sign(str: &str) {
1375            let err = JsonFloat::from_str(str).expect_err("should not parse");
1376            let expected = format!("string '{str}' includes a leading plus sign");
1377            assert_eq!(err.to_string(), expected);
1378        }
1379
1380        #[test_case("00.0"; "00d0")]
1381        #[test_case("-00.0"; "m00d0")]
1382        #[test_case("00"; "00")]
1383        #[test_case("00000000000")]
1384        #[test_case("-00"; "m00")]
1385        #[test_case("-00000000000"; "m00000000000")]
1386        #[test_case("03.14"; "03d14")]
1387        #[test_case("-03.14"; "m03d14")]
1388        #[test_case("03e14"; "03e14")]
1389        #[test_case("-03e14"; "m03e14")]
1390        #[test_case("00e14"; "00e14")]
1391        #[test_case("-00e14"; "m00e14")]
1392        fn invalid_float_strings_leading_zeros(str: &str) {
1393            let err = JsonFloat::from_str(str).expect_err("should not parse");
1394            let expected = format!("string '{str}' includes leading zeros");
1395            assert_eq!(err.to_string(), expected);
1396        }
1397    }
1398
1399    mod proptests {
1400        use super::super::*;
1401        use proptest::prelude::*;
1402
1403        proptest! {
1404            #[test]
1405            fn int_roundtrip(value in JSON_INT_LOWER_LIMIT..JSON_INT_UPPER_LIMIT) {
1406                let json_int = JsonInt::try_from(value).expect("within range");
1407                assert_eq!(json_int.as_i64(), value);
1408            }
1409
1410            #[test]
1411            fn uint_roundtrip(value in 0..JSON_UINT_UPPER_LIMIT) {
1412                let json_uint = JsonUInt::try_from(value).expect("within range");
1413                assert_eq!(json_uint.as_u64(), value);
1414            }
1415
1416            #[test]
1417            fn int_string_roundtrip(value in JSON_INT_LOWER_LIMIT..JSON_INT_UPPER_LIMIT) {
1418                let string = value.to_string();
1419                let json_int = JsonInt::from_str(&string).expect("valid string");
1420                assert_eq!(string, json_int.to_string())
1421            }
1422
1423            #[test]
1424            fn uint_string_roundtrip(value in 0..JSON_UINT_UPPER_LIMIT) {
1425                let string = value.to_string();
1426                let json_int = JsonUInt::from_str(&string).expect("valid string");
1427                assert_eq!(string, json_int.to_string())
1428            }
1429
1430            #[test]
1431            fn int_increment(value in JSON_INT_LOWER_LIMIT..(JSON_INT_UPPER_LIMIT - 1)) {
1432                let mut json_int = JsonInt::try_from(value).expect("within range");
1433                json_int.try_increment().expect("at most one below limit");
1434                assert_eq!(json_int.as_i64(), value + 1);
1435            }
1436
1437            #[test]
1438            fn uint_increment(value in 0..(JSON_UINT_UPPER_LIMIT - 1)) {
1439                let mut json_uint = JsonUInt::try_from(value).expect("within range");
1440                json_uint.try_increment().expect("at most one below limit");
1441                assert_eq!(json_uint.as_u64(), value + 1);
1442            }
1443        }
1444    }
1445}