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}