serde_yaml/
number.rs

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