serde_yaml/
number.rs

1use crate::de;
2use crate::error::{self, Error, ErrorImpl};
3use serde::de::{Unexpected, Visitor};
4use serde::{forward_to_deserialize_any, Deserialize, Deserializer, Serialize, Serializer};
5use std::cmp::Ordering;
6use std::fmt::{self, Display};
7use std::hash::{Hash, Hasher};
8use std::str::FromStr;
9
10/// Represents a YAML number, whether integer or floating point.
11#[derive(Clone, PartialEq, PartialOrd)]
12pub struct Number {
13    n: N,
14}
15
16// "N" is a prefix of "NegInt"... this is a false positive.
17// https://github.com/Manishearth/rust-clippy/issues/1241
18#[allow(clippy::enum_variant_names)]
19#[derive(Copy, Clone)]
20enum N {
21    PosInt(u64),
22    /// Always less than zero.
23    NegInt(i64),
24    /// May be infinite or NaN.
25    Float(f64),
26}
27
28impl Number {
29    /// Returns true if the `Number` is an integer between `i64::MIN` and
30    /// `i64::MAX`.
31    ///
32    /// For any Number on which `is_i64` returns true, `as_i64` is guaranteed to
33    /// return the integer value.
34    ///
35    /// ```
36    /// # fn main() -> serde_yaml::Result<()> {
37    /// let big = i64::MAX as u64 + 10;
38    /// let v: serde_yaml::Value = serde_yaml::from_str(r#"
39    /// a: 64
40    /// b: 9223372036854775817
41    /// c: 256.0
42    /// "#)?;
43    ///
44    /// assert!(v["a"].is_i64());
45    ///
46    /// // Greater than i64::MAX.
47    /// assert!(!v["b"].is_i64());
48    ///
49    /// // Numbers with a decimal point are not considered integers.
50    /// assert!(!v["c"].is_i64());
51    /// # Ok(())
52    /// # }
53    /// ```
54    #[inline]
55    #[allow(clippy::cast_sign_loss)]
56    pub fn is_i64(&self) -> bool {
57        match self.n {
58            N::PosInt(v) => v <= i64::max_value() as u64,
59            N::NegInt(_) => true,
60            N::Float(_) => false,
61        }
62    }
63
64    /// Returns true if the `Number` is an integer between zero and `u64::MAX`.
65    ///
66    /// For any Number on which `is_u64` returns true, `as_u64` is guaranteed to
67    /// return the integer value.
68    ///
69    /// ```
70    /// # fn main() -> serde_yaml::Result<()> {
71    /// let v: serde_yaml::Value = serde_yaml::from_str(r#"
72    /// a: 64
73    /// b: -64
74    /// c: 256.0
75    /// "#)?;
76    ///
77    /// assert!(v["a"].is_u64());
78    ///
79    /// // Negative integer.
80    /// assert!(!v["b"].is_u64());
81    ///
82    /// // Numbers with a decimal point are not considered integers.
83    /// assert!(!v["c"].is_u64());
84    /// # Ok(())
85    /// # }
86    /// ```
87    #[inline]
88    pub fn is_u64(&self) -> bool {
89        match self.n {
90            N::PosInt(_) => true,
91            N::NegInt(_) | N::Float(_) => false,
92        }
93    }
94
95    /// Returns true if the `Number` can be represented by f64.
96    ///
97    /// For any Number on which `is_f64` returns true, `as_f64` is guaranteed to
98    /// return the floating point value.
99    ///
100    /// Currently this function returns true if and only if both `is_i64` and
101    /// `is_u64` return false but this is not a guarantee in the future.
102    ///
103    /// ```
104    /// # fn main() -> serde_yaml::Result<()> {
105    /// let v: serde_yaml::Value = serde_yaml::from_str(r#"
106    /// a: 256.0
107    /// b: 64
108    /// c: -64
109    /// "#)?;
110    ///
111    /// assert!(v["a"].is_f64());
112    ///
113    /// // Integers.
114    /// assert!(!v["b"].is_f64());
115    /// assert!(!v["c"].is_f64());
116    /// # Ok(())
117    /// # }
118    /// ```
119    #[inline]
120    pub fn is_f64(&self) -> bool {
121        match self.n {
122            N::Float(_) => true,
123            N::PosInt(_) | N::NegInt(_) => false,
124        }
125    }
126
127    /// If the `Number` is an integer, represent it as i64 if possible. Returns
128    /// None otherwise.
129    ///
130    /// ```
131    /// # fn main() -> serde_yaml::Result<()> {
132    /// let big = i64::MAX as u64 + 10;
133    /// let v: serde_yaml::Value = serde_yaml::from_str(r#"
134    /// a: 64
135    /// b: 9223372036854775817
136    /// c: 256.0
137    /// "#)?;
138    ///
139    /// assert_eq!(v["a"].as_i64(), Some(64));
140    /// assert_eq!(v["b"].as_i64(), None);
141    /// assert_eq!(v["c"].as_i64(), None);
142    /// # Ok(())
143    /// # }
144    /// ```
145    #[inline]
146    pub fn as_i64(&self) -> Option<i64> {
147        match self.n {
148            N::PosInt(n) => {
149                if n <= i64::max_value() as u64 {
150                    Some(n as i64)
151                } else {
152                    None
153                }
154            }
155            N::NegInt(n) => Some(n),
156            N::Float(_) => None,
157        }
158    }
159
160    /// If the `Number` is an integer, represent it as u64 if possible. Returns
161    /// None otherwise.
162    ///
163    /// ```
164    /// # fn main() -> serde_yaml::Result<()> {
165    /// let v: serde_yaml::Value = serde_yaml::from_str(r#"
166    /// a: 64
167    /// b: -64
168    /// c: 256.0
169    /// "#)?;
170    ///
171    /// assert_eq!(v["a"].as_u64(), Some(64));
172    /// assert_eq!(v["b"].as_u64(), None);
173    /// assert_eq!(v["c"].as_u64(), None);
174    /// # Ok(())
175    /// # }
176    /// ```
177    #[inline]
178    pub fn as_u64(&self) -> Option<u64> {
179        match self.n {
180            N::PosInt(n) => Some(n),
181            N::NegInt(_) | N::Float(_) => None,
182        }
183    }
184
185    /// Represents the number as f64 if possible. Returns None otherwise.
186    ///
187    /// ```
188    /// # fn main() -> serde_yaml::Result<()> {
189    /// let v: serde_yaml::Value = serde_yaml::from_str(r#"
190    /// a: 256.0
191    /// b: 64
192    /// c: -64
193    /// "#)?;
194    ///
195    /// assert_eq!(v["a"].as_f64(), Some(256.0));
196    /// assert_eq!(v["b"].as_f64(), Some(64.0));
197    /// assert_eq!(v["c"].as_f64(), Some(-64.0));
198    /// # Ok(())
199    /// # }
200    /// ```
201    ///
202    /// ```
203    /// # fn main() -> serde_yaml::Result<()> {
204    /// let v: serde_yaml::Value = serde_yaml::from_str(".inf")?;
205    /// assert_eq!(v.as_f64(), Some(f64::INFINITY));
206    ///
207    /// let v: serde_yaml::Value = serde_yaml::from_str("-.inf")?;
208    /// assert_eq!(v.as_f64(), Some(f64::NEG_INFINITY));
209    ///
210    /// let v: serde_yaml::Value = serde_yaml::from_str(".nan")?;
211    /// assert!(v.as_f64().unwrap().is_nan());
212    /// # Ok(())
213    /// # }
214    /// ```
215    #[inline]
216    pub fn as_f64(&self) -> Option<f64> {
217        match self.n {
218            N::PosInt(n) => Some(n as f64),
219            N::NegInt(n) => Some(n as f64),
220            N::Float(n) => Some(n),
221        }
222    }
223
224    /// Returns true if this value is NaN and false otherwise.
225    ///
226    /// ```
227    /// # use serde_yaml::Number;
228    /// #
229    /// assert!(!Number::from(256.0).is_nan());
230    ///
231    /// assert!(Number::from(f64::NAN).is_nan());
232    ///
233    /// assert!(!Number::from(f64::INFINITY).is_nan());
234    ///
235    /// assert!(!Number::from(f64::NEG_INFINITY).is_nan());
236    ///
237    /// assert!(!Number::from(1).is_nan());
238    /// ```
239    #[inline]
240    pub fn is_nan(&self) -> bool {
241        match self.n {
242            N::PosInt(_) | N::NegInt(_) => false,
243            N::Float(f) => f.is_nan(),
244        }
245    }
246
247    /// Returns true if this value is positive infinity or negative infinity and
248    /// false otherwise.
249    ///
250    /// ```
251    /// # use serde_yaml::Number;
252    /// #
253    /// assert!(!Number::from(256.0).is_infinite());
254    ///
255    /// assert!(!Number::from(f64::NAN).is_infinite());
256    ///
257    /// assert!(Number::from(f64::INFINITY).is_infinite());
258    ///
259    /// assert!(Number::from(f64::NEG_INFINITY).is_infinite());
260    ///
261    /// assert!(!Number::from(1).is_infinite());
262    /// ```
263    #[inline]
264    pub fn is_infinite(&self) -> bool {
265        match self.n {
266            N::PosInt(_) | N::NegInt(_) => false,
267            N::Float(f) => f.is_infinite(),
268        }
269    }
270
271    /// Returns true if this number is neither infinite nor NaN.
272    ///
273    /// ```
274    /// # use serde_yaml::Number;
275    /// #
276    /// assert!(Number::from(256.0).is_finite());
277    ///
278    /// assert!(!Number::from(f64::NAN).is_finite());
279    ///
280    /// assert!(!Number::from(f64::INFINITY).is_finite());
281    ///
282    /// assert!(!Number::from(f64::NEG_INFINITY).is_finite());
283    ///
284    /// assert!(Number::from(1).is_finite());
285    /// ```
286    #[inline]
287    pub fn is_finite(&self) -> bool {
288        match self.n {
289            N::PosInt(_) | N::NegInt(_) => true,
290            N::Float(f) => f.is_finite(),
291        }
292    }
293}
294
295impl Display for Number {
296    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
297        match self.n {
298            N::PosInt(i) => formatter.write_str(itoa::Buffer::new().format(i)),
299            N::NegInt(i) => formatter.write_str(itoa::Buffer::new().format(i)),
300            N::Float(f) if f.is_nan() => formatter.write_str(".nan"),
301            N::Float(f) if f.is_infinite() => {
302                if f.is_sign_negative() {
303                    formatter.write_str("-.inf")
304                } else {
305                    formatter.write_str(".inf")
306                }
307            }
308            N::Float(f) => formatter.write_str(ryu::Buffer::new().format_finite(f)),
309        }
310    }
311}
312
313impl FromStr for Number {
314    type Err = Error;
315
316    fn from_str(repr: &str) -> Result<Self, Self::Err> {
317        if let Ok(result) = de::visit_int(NumberVisitor, repr) {
318            return result;
319        }
320        if !de::digits_but_not_number(repr) {
321            if let Some(float) = de::parse_f64(repr) {
322                return Ok(float.into());
323            }
324        }
325        Err(error::new(ErrorImpl::FailedToParseNumber))
326    }
327}
328
329impl PartialEq for N {
330    fn eq(&self, other: &N) -> bool {
331        match (*self, *other) {
332            (N::PosInt(a), N::PosInt(b)) => a == b,
333            (N::NegInt(a), N::NegInt(b)) => a == b,
334            (N::Float(a), N::Float(b)) => {
335                if a.is_nan() && b.is_nan() {
336                    // YAML only has one NaN;
337                    // the bit representation isn't preserved
338                    true
339                } else {
340                    a == b
341                }
342            }
343            _ => false,
344        }
345    }
346}
347
348impl PartialOrd for N {
349    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
350        match (*self, *other) {
351            (N::Float(a), N::Float(b)) => {
352                if a.is_nan() && b.is_nan() {
353                    // YAML only has one NaN
354                    Some(Ordering::Equal)
355                } else {
356                    a.partial_cmp(&b)
357                }
358            }
359            _ => Some(self.total_cmp(other)),
360        }
361    }
362}
363
364impl N {
365    fn total_cmp(&self, other: &Self) -> Ordering {
366        match (*self, *other) {
367            (N::PosInt(a), N::PosInt(b)) => a.cmp(&b),
368            (N::NegInt(a), N::NegInt(b)) => a.cmp(&b),
369            // negint is always less than zero
370            (N::NegInt(_), N::PosInt(_)) => Ordering::Less,
371            (N::PosInt(_), N::NegInt(_)) => Ordering::Greater,
372            (N::Float(a), N::Float(b)) => a.partial_cmp(&b).unwrap_or_else(|| {
373                // arbitrarily sort the NaN last
374                if !a.is_nan() {
375                    Ordering::Less
376                } else if !b.is_nan() {
377                    Ordering::Greater
378                } else {
379                    Ordering::Equal
380                }
381            }),
382            // arbitrarily sort integers below floats
383            // FIXME: maybe something more sensible?
384            (_, N::Float(_)) => Ordering::Less,
385            (N::Float(_), _) => Ordering::Greater,
386        }
387    }
388}
389
390impl Number {
391    pub(crate) fn total_cmp(&self, other: &Self) -> Ordering {
392        self.n.total_cmp(&other.n)
393    }
394}
395
396impl Serialize for Number {
397    #[inline]
398    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
399    where
400        S: Serializer,
401    {
402        match self.n {
403            N::PosInt(i) => serializer.serialize_u64(i),
404            N::NegInt(i) => serializer.serialize_i64(i),
405            N::Float(f) => serializer.serialize_f64(f),
406        }
407    }
408}
409
410struct NumberVisitor;
411
412impl<'de> Visitor<'de> for NumberVisitor {
413    type Value = Number;
414
415    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
416        formatter.write_str("a number")
417    }
418
419    #[inline]
420    fn visit_i64<E>(self, value: i64) -> Result<Number, E> {
421        Ok(value.into())
422    }
423
424    #[inline]
425    fn visit_u64<E>(self, value: u64) -> Result<Number, E> {
426        Ok(value.into())
427    }
428
429    #[inline]
430    fn visit_f64<E>(self, value: f64) -> Result<Number, E> {
431        Ok(value.into())
432    }
433}
434
435impl<'de> Deserialize<'de> for Number {
436    #[inline]
437    fn deserialize<D>(deserializer: D) -> Result<Number, D::Error>
438    where
439        D: Deserializer<'de>,
440    {
441        deserializer.deserialize_any(NumberVisitor)
442    }
443}
444
445impl<'de> Deserializer<'de> for Number {
446    type Error = Error;
447
448    #[inline]
449    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Error>
450    where
451        V: Visitor<'de>,
452    {
453        match self.n {
454            N::PosInt(i) => visitor.visit_u64(i),
455            N::NegInt(i) => visitor.visit_i64(i),
456            N::Float(f) => visitor.visit_f64(f),
457        }
458    }
459
460    forward_to_deserialize_any! {
461        bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
462        bytes byte_buf option unit unit_struct newtype_struct seq tuple
463        tuple_struct map struct enum identifier ignored_any
464    }
465}
466
467impl<'de, 'a> Deserializer<'de> for &'a Number {
468    type Error = Error;
469
470    #[inline]
471    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Error>
472    where
473        V: Visitor<'de>,
474    {
475        match self.n {
476            N::PosInt(i) => visitor.visit_u64(i),
477            N::NegInt(i) => visitor.visit_i64(i),
478            N::Float(f) => visitor.visit_f64(f),
479        }
480    }
481
482    forward_to_deserialize_any! {
483        bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
484        bytes byte_buf option unit unit_struct newtype_struct seq tuple
485        tuple_struct map struct enum identifier ignored_any
486    }
487}
488
489macro_rules! from_signed {
490    ($($signed_ty:ident)*) => {
491        $(
492            impl From<$signed_ty> for Number {
493                #[inline]
494                #[allow(clippy::cast_sign_loss)]
495                fn from(i: $signed_ty) -> Self {
496                    if i < 0 {
497                        Number { n: N::NegInt(i as i64) }
498                    } else {
499                        Number { n: N::PosInt(i as u64) }
500                    }
501                }
502            }
503        )*
504    };
505}
506
507macro_rules! from_unsigned {
508    ($($unsigned_ty:ident)*) => {
509        $(
510            impl From<$unsigned_ty> for Number {
511                #[inline]
512                fn from(u: $unsigned_ty) -> Self {
513                    Number { n: N::PosInt(u as u64) }
514                }
515            }
516        )*
517    };
518}
519
520from_signed!(i8 i16 i32 i64 isize);
521from_unsigned!(u8 u16 u32 u64 usize);
522
523impl From<f32> for Number {
524    fn from(f: f32) -> Self {
525        Number::from(f as f64)
526    }
527}
528
529impl From<f64> for Number {
530    fn from(mut f: f64) -> Self {
531        if f.is_nan() {
532            // Destroy NaN sign, signaling, and payload. YAML only has one NaN.
533            f = f64::NAN.copysign(1.0);
534        }
535        Number { n: N::Float(f) }
536    }
537}
538
539// This is fine, because we don't _really_ implement hash for floats
540// all other hash functions should work as expected
541#[allow(clippy::derived_hash_with_manual_eq)]
542impl Hash for Number {
543    fn hash<H: Hasher>(&self, state: &mut H) {
544        match self.n {
545            N::Float(_) => {
546                // you should feel bad for using f64 as a map key
547                3.hash(state);
548            }
549            N::PosInt(u) => u.hash(state),
550            N::NegInt(i) => i.hash(state),
551        }
552    }
553}
554
555pub(crate) fn unexpected(number: &Number) -> Unexpected {
556    match number.n {
557        N::PosInt(u) => Unexpected::Unsigned(u),
558        N::NegInt(i) => Unexpected::Signed(i),
559        N::Float(f) => Unexpected::Float(f),
560    }
561}