use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Visitor};
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub enum Number {
PositiveInt(u64),
NegativeInt(i64),
Float(f64),
}
impl Number {
pub fn as_unsigned_int(&self) -> Option<u64> {
match self {
Number::PositiveInt(v) => Some(*v),
Number::NegativeInt(v) => (*v).try_into().ok(),
_ => None,
}
}
pub fn as_signed_int(&self) -> Option<i64> {
match self {
Number::PositiveInt(v) => i64::try_from(*v).ok(),
Number::NegativeInt(v) => Some(*v),
_ => None,
}
}
pub fn as_float(&self) -> Option<f64> {
match self {
Number::Float(v) => Some(*v),
_ => None,
}
}
}
impl From<u64> for Number {
fn from(value: u64) -> Self {
Number::PositiveInt(value)
}
}
impl From<i64> for Number {
fn from(value: i64) -> Self {
u64::try_from(value)
.map(Number::PositiveInt)
.unwrap_or_else(|_| Number::NegativeInt(value))
}
}
macro_rules! impl_from_num {
($from:ident, $cast:path) => {
impl From<$from> for Number {
fn from(value: $from) -> Self {
Self::from(value as $cast)
}
}
};
}
impl_from_num!(u8, u64);
impl_from_num!(u16, u64);
impl_from_num!(u32, u64);
impl_from_num!(i8, i64);
impl_from_num!(i16, i64);
impl_from_num!(i32, i64);
impl TryFrom<usize> for Number {
type Error = core::num::TryFromIntError;
fn try_from(value: usize) -> Result<Self, Self::Error> {
u64::try_from(value).map(Number::PositiveInt)
}
}
impl TryFrom<isize> for Number {
type Error = core::num::TryFromIntError;
fn try_from(value: isize) -> Result<Self, Self::Error> {
if let Ok(v) = i64::try_from(value) {
return Ok(Number::from(v));
}
u64::try_from(value).map(Self::from)
}
}
impl From<f32> for Number {
fn from(value: f32) -> Self {
Self::from(value as f64)
}
}
impl From<f64> for Number {
fn from(value: f64) -> Self {
Self::Float(value)
}
}
impl Serialize for Number {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Number::PositiveInt(n) => serializer.serialize_u64(*n),
Number::NegativeInt(n) => serializer.serialize_i64(*n),
Number::Float(n) => serializer.serialize_f64(*n),
}
}
}
impl<'de> Deserialize<'de> for Number {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct NumberVisitor;
impl Visitor<'_> for NumberVisitor {
type Value = Number;
fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
formatter.write_str("a number")
}
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Number::from(v))
}
fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Number::from(v))
}
fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Number::from(v))
}
}
deserializer.deserialize_any(NumberVisitor)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::from_slice;
use rstest::rstest;
#[rstest]
#[case([0x05],5)]
#[case([0xcd, 0xff, 0xff],u16::MAX.into())]
#[case([0xce, 0xff, 0xff,0xff,0xff],u32::MAX.into())]
#[case([0xcf, 0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff],u64::MAX)]
fn decode_unsigned_int<Buf: AsRef<[u8]>>(#[case] input: Buf, #[case] expected: u64) {
let num = from_slice::<Number>(input.as_ref()).unwrap();
assert_eq!(num, Number::PositiveInt(expected));
}
#[rstest]
#[case([0xe0],-32)]
#[case([0xd0, 0x80],i8::MIN.into())]
#[case([0xd1, 0x80, 0x00],i16::MIN.into())]
#[case([0xd2, 0x80, 0x00, 0x00, 0x00],i32::MIN.into())]
#[case([0xd3, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],i64::MIN)]
fn decode_signed_int<Buf: AsRef<[u8]>>(#[case] input: Buf, #[case] expected: i64) {
let num = from_slice::<Number>(input.as_ref()).unwrap();
assert_eq!(num, Number::NegativeInt(expected));
}
#[rstest]
#[case([0xca, 0x42, 0xf6, 0xe9, 0x79],123.456)]
#[case([0xcb, 0x40, 0xfe, 0x24, 0x0c, 0x9f, 0xcb, 0x0c, 0x02],123456.789012)]
fn decode_float<Buf: AsRef<[u8]>>(#[case] input: Buf, #[case] expected: f64) {
let num = from_slice::<Number>(input.as_ref()).unwrap();
match num {
Number::Float(n) => {
let diff = (n - expected).abs();
assert!(diff < (1e-5))
}
_ => {
panic!("Err")
}
}
}
}