Lines
41.58 %
Functions
18.11 %
Branches
100 %
use core::{
cmp::{Eq, Ordering},
hash::{Hash, Hasher},
};
use crate::error::{Error, Result};
use serde::{de::Visitor, Serialize, Serializer};
/// A wrapper for any numeric primitive type in Rust.
///
/// Some varints of the `Number` enum are enabled by features:
/// - `Number::I128` and `Number::U128` by the `integer128` feature
/// To ensure that feature unification does not break `match`ing over `Number`,
/// the `Number` enum is non-exhaustive.
/// <details>
/// <summary>Exhaustively matching on <code>Number</code> in tests</summary>
/// If you want to ensure that you exhaustively handle every variant, you can
/// match on the hidden `Number::__NonExhaustive(x)` variant by using the
/// `x.never() -> !` method.
/// <div class="warning">
/// Matching on this variant means that your code may break when RON is
/// upgraded or when feature unification enables further variants in the
/// <code>Number</code> enum than your code expects.
/// </div>
/// It is your responsibility to only *ever* match on `Number::__NonExhaustive`
/// inside tests, e.g. by using `#[cfg(test)]` on the particular match arm, to
/// ensure that only your tests break (e.g. in CI) when your code is not
/// exhaustively matching on every variant, e.g. after a version upgrade or
/// feature unification.
/// </details>
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Hash, Ord)]
#[cfg_attr(doc, non_exhaustive)]
pub enum Number {
I8(i8),
I16(i16),
I32(i32),
I64(i64),
#[cfg(feature = "integer128")]
I128(i128),
U8(u8),
U16(u16),
U32(u32),
U64(u64),
U128(u128),
F32(F32),
F64(F64),
#[cfg(not(doc))]
#[allow(private_interfaces)]
__NonExhaustive(private::Never),
}
mod private {
#[derive(Debug, PartialEq, PartialOrd, Eq, Hash, Ord)]
enum _Never {}
pub struct Never {
never: &'static _Never,
impl Never {
pub fn never(self) -> ! {
match *self.never {}
#[cfg(not(feature = "integer128"))]
/// ```compile_fail
/// # use ron::Number;
/// fn match_number(x: Number) {
/// match x {
/// Number::I8(v) => println!("i8: {}", v),
/// Number::I16(v) => println!("i16: {}", v),
/// Number::I32(v) => println!("i32: {}", v),
/// Number::I64(v) => println!("i64: {}", v),
/// Number::U8(v) => println!("u8: {}", v),
/// Number::U16(v) => println!("u16: {}", v),
/// Number::U32(v) => println!("u32: {}", v),
/// Number::U64(v) => println!("u64: {}", v),
/// Number::F32(v) => println!("f32: {}", v.0),
/// Number::F64(v) => println!("f64: {}", v.0),
/// }
/// ```
fn _assert_non_exhaustive_check_fails_not_integer128() {}
/// Number::I128(v) => println!("i128: {}", v),
/// Number::U128(v) => println!("u128: {}", v),
fn _assert_non_exhaustive_check_fails_integer128() {}
impl Serialize for Number {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
Self::I8(v) => serializer.serialize_i8(*v),
Self::I16(v) => serializer.serialize_i16(*v),
Self::I32(v) => serializer.serialize_i32(*v),
Self::I64(v) => serializer.serialize_i64(*v),
Self::I128(v) => serializer.serialize_i128(*v),
Self::U8(v) => serializer.serialize_u8(*v),
Self::U16(v) => serializer.serialize_u16(*v),
Self::U32(v) => serializer.serialize_u32(*v),
Self::U64(v) => serializer.serialize_u64(*v),
Self::U128(v) => serializer.serialize_u128(*v),
Self::F32(v) => serializer.serialize_f32(v.get()),
Self::F64(v) => serializer.serialize_f64(v.get()),
Self::__NonExhaustive(never) => never.never(),
impl Number {
pub fn visit<'de, V: Visitor<'de>, E: serde::de::Error>(
&self,
visitor: V,
) -> Result<V::Value, E> {
Self::I8(v) => visitor.visit_i8(*v),
Self::I16(v) => visitor.visit_i16(*v),
Self::I32(v) => visitor.visit_i32(*v),
Self::I64(v) => visitor.visit_i64(*v),
Self::I128(v) => visitor.visit_i128(*v),
Self::U8(v) => visitor.visit_u8(*v),
Self::U16(v) => visitor.visit_u16(*v),
Self::U32(v) => visitor.visit_u32(*v),
Self::U64(v) => visitor.visit_u64(*v),
Self::U128(v) => visitor.visit_u128(*v),
Self::F32(v) => visitor.visit_f32(v.get()),
Self::F64(v) => visitor.visit_f64(v.get()),
macro_rules! float_ty {
($ty:ident($float:ty)) => {
#[doc = concat!(
"A wrapper for [`", stringify!($float), "`], which implements [`Eq`], ",
"[`Hash`] and [`Ord`] using [`", stringify!($float), "::total_cmp`] ",
"for a total order comparison",
)]
#[derive(Copy, Clone, Debug)] // GRCOV_EXCL_LINE
pub struct $ty(pub $float);
impl $ty {
#[doc = concat!("Construct a new [`", stringify!($ty), "`].")]
#[must_use]
pub fn new(v: $float) -> Self {
Self(v)
#[doc = concat!("Returns the wrapped [`", stringify!($float), "`].")]
pub fn get(self) -> $float {
self.0
impl From<$float> for $ty {
fn from(v: $float) -> Self {
Self::new(v)
/// Partial equality comparison
"In order to be able to use [`", stringify!($ty), "`] as a mapping key, ",
"floating values use [`", stringify!($float), "::total_cmp`] for a total ",
"order comparison.",
/// See the [`Ord`] implementation.
impl PartialEq for $ty {
fn eq(&self, other: &Self) -> bool {
self.cmp(other).is_eq()
/// Equality comparison
impl Eq for $ty {}
impl Hash for $ty {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.to_bits().hash(state);
/// Partial ordering comparison
impl PartialOrd for $ty {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
/// Ordering comparison
#[doc = concat!("use ron::value::", stringify!($ty), ";")]
"assert!(", stringify!($ty), "::new(", stringify!($float), "::NAN) > ",
stringify!($ty), "::new(", stringify!($float), "::INFINITY));",
"assert!(", stringify!($ty), "::new(-", stringify!($float), "::NAN) < ",
stringify!($ty), "::new(", stringify!($float), "::NEG_INFINITY));",
"assert!(", stringify!($ty), "::new(", stringify!($float), "::NAN) == ",
stringify!($ty), "::new(", stringify!($float), "::NAN));",
impl Ord for $ty {
fn cmp(&self, other: &Self) -> Ordering {
self.0.total_cmp(&other.0)
float_ty! { F32(f32) }
float_ty! { F64(f64) }
/// Construct a new number.
pub fn new(v: impl Into<Number>) -> Self {
v.into()
/// Returns the [`f64`] representation of the [`Number`] regardless of
/// whether the number is stored as a float or integer.
/// # Example
/// # use ron::value::Number;
/// let i = Number::new(5);
/// let f = Number::new(2.0);
/// assert_eq!(i.into_f64(), 5.0);
/// assert_eq!(f.into_f64(), 2.0);
pub fn into_f64(self) -> f64 {
#[allow(clippy::cast_precision_loss)]
Self::I8(v) => f64::from(v),
Self::I16(v) => f64::from(v),
Self::I32(v) => f64::from(v),
Self::I64(v) => v as f64,
Self::I128(v) => v as f64,
Self::U8(v) => f64::from(v),
Self::U16(v) => f64::from(v),
Self::U32(v) => f64::from(v),
Self::U64(v) => v as f64,
Self::U128(v) => v as f64,
Self::F32(v) => f64::from(v.get()),
Self::F64(v) => v.get(),
macro_rules! number_from_impl {
(Number::$variant:ident($wrap:ident($ty:ty))) => {
impl From<$ty> for Number {
fn from(v: $ty) -> Number {
Number::$variant($wrap(v))
(Number::$variant:ident($ty:ty)) => {
Number::$variant(v)
number_from_impl! { Number::I8(i8) }
number_from_impl! { Number::I16(i16) }
number_from_impl! { Number::I32(i32) }
number_from_impl! { Number::I64(i64) }
number_from_impl! { Number::I128(i128) }
number_from_impl! { Number::U8(u8) }
number_from_impl! { Number::U16(u16) }
number_from_impl! { Number::U32(u32) }
number_from_impl! { Number::U64(u64) }
number_from_impl! { Number::U128(u128) }
number_from_impl! { Number::F32(F32(f32)) }
number_from_impl! { Number::F64(F64(f64)) }
/// A serializer that only accepts numeric values, returning a [`Number`].
/// Used by [`crate::ser::RangeCompound`] to detect whether range bounds are numeric.
#[allow(clippy::module_name_repetitions)]
pub struct NumberSerializer;
impl serde::Serializer for NumberSerializer {
type Ok = Number;
type Error = Error;
type SerializeSeq = serde::ser::Impossible<Number, Error>;
type SerializeTuple = serde::ser::Impossible<Number, Error>;
type SerializeTupleStruct = serde::ser::Impossible<Number, Error>;
type SerializeTupleVariant = serde::ser::Impossible<Number, Error>;
type SerializeMap = serde::ser::Impossible<Number, Error>;
type SerializeStruct = serde::ser::Impossible<Number, Error>;
type SerializeStructVariant = serde::ser::Impossible<Number, Error>;
fn serialize_i8(self, v: i8) -> Result<Number> {
Ok(Number::new(v))
fn serialize_i16(self, v: i16) -> Result<Number> {
fn serialize_i32(self, v: i32) -> Result<Number> {
fn serialize_i64(self, v: i64) -> Result<Number> {
fn serialize_u8(self, v: u8) -> Result<Number> {
fn serialize_u16(self, v: u16) -> Result<Number> {
fn serialize_u32(self, v: u32) -> Result<Number> {
fn serialize_u64(self, v: u64) -> Result<Number> {
fn serialize_f32(self, v: f32) -> Result<Number> {
fn serialize_f64(self, v: f64) -> Result<Number> {
fn serialize_i128(self, v: i128) -> Result<Number> {
fn serialize_u128(self, v: u128) -> Result<Number> {
fn serialize_bool(self, _: bool) -> Result<Number> {
Err(Error::InvalidValueForType {
expected: alloc::string::String::from("number"),
found: alloc::string::String::from("bool"),
})
fn serialize_char(self, _: char) -> Result<Number> {
found: alloc::string::String::from("char"),
fn serialize_str(self, _: &str) -> Result<Number> {
found: alloc::string::String::from("string"),
fn serialize_bytes(self, _: &[u8]) -> Result<Number> {
found: alloc::string::String::from("bytes"),
fn serialize_none(self) -> Result<Number> {
found: alloc::string::String::from("None"),
fn serialize_some<T: ?Sized + Serialize>(self, _: &T) -> Result<Number> {
found: alloc::string::String::from("Some"),
fn serialize_unit(self) -> Result<Number> {
found: alloc::string::String::from("unit"),
fn serialize_unit_struct(self, _: &'static str) -> Result<Number> {
found: alloc::string::String::from("unit struct"),
fn serialize_unit_variant(self, _: &'static str, _: u32, _: &'static str) -> Result<Number> {
found: alloc::string::String::from("unit variant"),
fn serialize_newtype_struct<T: ?Sized + Serialize>(
self,
_: &'static str,
_: &T,
) -> Result<Number> {
found: alloc::string::String::from("newtype struct"),
fn serialize_newtype_variant<T: ?Sized + Serialize>(
_: u32,
found: alloc::string::String::from("newtype variant"),
fn serialize_seq(self, _: Option<usize>) -> Result<Self::SerializeSeq> {
found: alloc::string::String::from("seq"),
fn serialize_tuple(self, _: usize) -> Result<Self::SerializeTuple> {
found: alloc::string::String::from("tuple"),
fn serialize_tuple_struct(
_: usize,
) -> Result<Self::SerializeTupleStruct> {
found: alloc::string::String::from("tuple struct"),
fn serialize_tuple_variant(
) -> Result<Self::SerializeTupleVariant> {
found: alloc::string::String::from("tuple variant"),
fn serialize_map(self, _: Option<usize>) -> Result<Self::SerializeMap> {
found: alloc::string::String::from("map"),
fn serialize_struct(self, _: &'static str, _: usize) -> Result<Self::SerializeStruct> {
found: alloc::string::String::from("struct"),
fn serialize_struct_variant(
) -> Result<Self::SerializeStructVariant> {
found: alloc::string::String::from("struct variant"),
pub(crate) struct NumberDeserializer(pub(crate) Number);
impl<'de> serde::de::Deserializer<'de> for NumberDeserializer {
type Error = crate::error::Error;
fn deserialize_any<V: serde::de::Visitor<'de>>(
) -> crate::error::Result<V::Value> {
self.0.visit(visitor)
serde::forward_to_deserialize_any! {
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64
char str string bytes byte_buf option unit unit_struct
newtype_struct seq tuple tuple_struct map struct enum identifier ignored_any
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_nan() {
assert_eq!(F32(f32::NAN), F32(f32::NAN));
assert_eq!(F32(-f32::NAN), F32(-f32::NAN));
assert_ne!(F32(f32::NAN), F32(-f32::NAN));
#[cfg(feature = "std")]
fn test_nan_hash() {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
fn hash<T: Hash>(v: &T) -> u64 {
let mut state = DefaultHasher::new();
v.hash(&mut state);
state.finish()
assert_eq!(hash(&F32(f32::NAN)), hash(&F32(f32::NAN)));
assert_eq!(hash(&F32(-f32::NAN)), hash(&F32(-f32::NAN)));
assert_ne!(hash(&F32(f32::NAN)), hash(&F32(-f32::NAN)));
fn test_partial_ord() {
assert!(F32(f32::NAN) > F32(f32::INFINITY));
assert!(F32(-f32::NAN) < F32(f32::NEG_INFINITY));
assert!(F32(f32::NAN) == F32(f32::NAN));