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