1 use crate::de;
2 use crate::error::{self, Error, ErrorImpl};
3 use serde::de::{Unexpected, Visitor};
4 use serde::{forward_to_deserialize_any, Deserialize, Deserializer, Serialize, Serializer};
5 use std::cmp::Ordering;
6 use std::fmt::{self, Display};
7 use std::hash::{Hash, Hasher};
8 use std::str::FromStr;
9 
10 /// Represents a YAML number, whether integer or floating point.
11 #[derive(Clone, PartialEq, PartialOrd)]
12 pub 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)]
20 enum N {
21     PosInt(u64),
22     /// Always less than zero.
23     NegInt(i64),
24     /// May be infinite or NaN.
25     Float(f64),
26 }
27 
28 impl 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)]
is_i64(&self) -> bool56     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]
is_u64(&self) -> bool88     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]
is_f64(&self) -> bool120     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]
as_i64(&self) -> Option<i64>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]
as_u64(&self) -> Option<u64>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]
as_f64(&self) -> Option<f64>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]
is_nan(&self) -> bool240     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]
is_infinite(&self) -> bool264     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]
is_finite(&self) -> bool287     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 
295 impl Display for Number {
fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result296     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 
313 impl FromStr for Number {
314     type Err = Error;
315 
from_str(repr: &str) -> Result<Self, Self::Err>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 
329 impl PartialEq for N {
eq(&self, other: &N) -> bool330     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 
348 impl PartialOrd for N {
partial_cmp(&self, other: &Self) -> Option<Ordering>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 
364 impl N {
total_cmp(&self, other: &Self) -> Ordering365     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 
390 impl Number {
total_cmp(&self, other: &Self) -> Ordering391     pub(crate) fn total_cmp(&self, other: &Self) -> Ordering {
392         self.n.total_cmp(&other.n)
393     }
394 }
395 
396 impl Serialize for Number {
397     #[inline]
serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer,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 
410 struct NumberVisitor;
411 
412 impl<'de> Visitor<'de> for NumberVisitor {
413     type Value = Number;
414 
expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result415     fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
416         formatter.write_str("a number")
417     }
418 
419     #[inline]
visit_i64<E>(self, value: i64) -> Result<Number, E>420     fn visit_i64<E>(self, value: i64) -> Result<Number, E> {
421         Ok(value.into())
422     }
423 
424     #[inline]
visit_u64<E>(self, value: u64) -> Result<Number, E>425     fn visit_u64<E>(self, value: u64) -> Result<Number, E> {
426         Ok(value.into())
427     }
428 
429     #[inline]
visit_f64<E>(self, value: f64) -> Result<Number, E>430     fn visit_f64<E>(self, value: f64) -> Result<Number, E> {
431         Ok(value.into())
432     }
433 }
434 
435 impl<'de> Deserialize<'de> for Number {
436     #[inline]
deserialize<D>(deserializer: D) -> Result<Number, D::Error> where D: Deserializer<'de>,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 
445 impl<'de> Deserializer<'de> for Number {
446     type Error = Error;
447 
448     #[inline]
deserialize_any<V>(self, visitor: V) -> Result<V::Value, Error> where V: Visitor<'de>,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 
467 impl<'de, 'a> Deserializer<'de> for &'a Number {
468     type Error = Error;
469 
470     #[inline]
deserialize_any<V>(self, visitor: V) -> Result<V::Value, Error> where V: Visitor<'de>,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 
489 macro_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 
507 macro_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 
520 from_signed!(i8 i16 i32 i64 isize);
521 from_unsigned!(u8 u16 u32 u64 usize);
522 
523 impl From<f32> for Number {
from(f: f32) -> Self524     fn from(f: f32) -> Self {
525         Number::from(f as f64)
526     }
527 }
528 
529 impl From<f64> for Number {
from(mut f: f64) -> Self530     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)]
542 impl Hash for Number {
hash<H: Hasher>(&self, state: &mut H)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 
unexpected(number: &Number) -> Unexpected555 pub(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 }
562