From 6ee541d5f2caa131ec0c78c0a47ca6a06334a6b8 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Wed, 24 Jan 2024 09:01:54 +0100 Subject: [PATCH] login: parse helpers for floats Of course PVE also stringifies those in the API, duh... Signed-off-by: Wolfgang Bumiller --- proxmox-login/src/parse.rs | 132 +++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/proxmox-login/src/parse.rs b/proxmox-login/src/parse.rs index b2653521..65221939 100644 --- a/proxmox-login/src/parse.rs +++ b/proxmox-login/src/parse.rs @@ -115,6 +115,7 @@ where macro_rules! integer_helper { ($ty:ident, $deserialize_name:ident, $trait: ident, $from_name:ident, $visitor:ident) => { + #[doc(hidden)] pub trait $trait: Sized + Default { fn $from_name(value: $ty) -> Self; } @@ -237,3 +238,134 @@ integer_helper!(i8, deserialize_i8, FromI8, from_i8, I8Visitor); integer_helper!(i16, deserialize_i16, FromI16, from_i16, I16Visitor); integer_helper!(i32, deserialize_i32, FromI32, from_i32, I32Visitor); integer_helper!(i64, deserialize_i64, FromI64, from_i64, I64Visitor); + +// float helpers: + +macro_rules! float_helper { + ($ty:ident, $deserialize_name:ident, $visitor:ident) => { + pub fn $deserialize_name<'de, D, T>(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + T: FromF64, + { + deserializer.deserialize_any($visitor::::new()) + } + + struct $visitor(std::marker::PhantomData); + + impl $visitor { + fn new() -> Self { + Self(std::marker::PhantomData) + } + } + + impl<'de, T: FromF64> serde::de::DeserializeSeed<'de> for $visitor { + type Value = T; + + fn deserialize(self, deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + $deserialize_name(deserializer) + } + } + + impl<'de, T> serde::de::Visitor<'de> for $visitor + where + T: FromF64, + { + type Value = T; + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(concat!("a ", stringify!($ty), "-ish...")) + } + + fn visit_some(self, deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_any(self) + } + + fn visit_none(self) -> Result { + Ok(Default::default()) + } + + fn visit_f64(self, value: f64) -> Result { + Ok(T::from_f64(value)) + } + + fn visit_i128(self, value: i128) -> Result { + let conv = value as f64; + if conv as i128 == value { + Ok(T::from_f64(conv)) + } else { + Err(E::invalid_value(Unexpected::Other("i128"), &self)) + } + } + + fn visit_i64(self, value: i64) -> Result { + let conv = value as f64; + if conv as i64 == value { + Ok(T::from_f64(conv)) + } else { + Err(E::invalid_value(Unexpected::Signed(value), &self)) + } + } + + fn visit_u128(self, value: u128) -> Result { + let conv = value as f64; + if conv as u128 == value { + Ok(T::from_f64(conv)) + } else { + Err(E::invalid_value(Unexpected::Other("u128"), &self)) + } + } + + fn visit_u64(self, value: u64) -> Result { + let conv = value as f64; + if conv as u64 == value { + Ok(T::from_f64(conv)) + } else { + Err(E::invalid_value(Unexpected::Unsigned(value), &self)) + } + } + + fn visit_str(self, value: &str) -> Result { + let value = value + .parse() + .map_err(|_| E::invalid_value(Unexpected::Str(value), &self))?; + self.visit_f64(value) + } + } + }; +} + +#[doc(hidden)] +pub trait FromF64: Sized + Default { + fn from_f64(value: f64) -> Self; +} + +impl FromF64 for f32 { + #[inline(always)] + fn from_f64(f: f64) -> f32 { + f as f32 + } +} + +impl FromF64 for f64 { + #[inline(always)] + fn from_f64(f: f64) -> f64 { + f + } +} + +impl FromF64 for Option { + #[inline(always)] + fn from_f64(f: f64) -> Option { + Some(T::from_f64(f)) + } +} + +float_helper!(f32, deserialize_f32, F32Visitor); +float_helper!(f64, deserialize_f64, F64Visitor);