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