use std::default;
use std::fmt;
use std::marker::PhantomData;
use std::str::FromStr;
use serde::de::{self, Visitor};
use serde::{Deserialize, Deserializer};
#[derive(Debug, Clone, Copy)]
pub struct FloatOrString(f64);
impl<'de> Deserialize<'de> for FloatOrString {
fn deserialize<D>(deserializer: D) -> Result<FloatOrString, D::Error>
where
D: Deserializer<'de>,
{
struct StringOrNum<T>(PhantomData<fn() -> T>);
impl<'de, T> Visitor<'de> for StringOrNum<T>
where
T: Deserialize<'de>
+ FromStr<Err = <f64 as FromStr>::Err>
+ From<u64>
+ From<i64>
+ From<f64>,
{
type Value = T;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("string or number")
}
fn visit_str<E>(self, value: &str) -> Result<T, E>
where
E: de::Error,
{
Ok(FromStr::from_str(value).unwrap())
}
fn visit_u64<E>(self, value: u64) -> Result<T, E>
where
E: de::Error,
{
Ok(From::from(value))
}
fn visit_i64<E>(self, value: i64) -> Result<T, E>
where
E: de::Error,
{
Ok(From::from(value))
}
fn visit_f64<E>(self, value: f64) -> Result<T, E>
where
E: de::Error,
{
Ok(From::from(value))
}
fn visit_unit<E>(self) -> Result<T, E>
where
E: de::Error,
{
Ok(From::from(std::f64::NAN))
}
}
deserializer.deserialize_any(StringOrNum(PhantomData))
}
}
impl FromStr for FloatOrString {
type Err = <f64 as FromStr>::Err;
fn from_str(s: &str) -> Result<FloatOrString, Self::Err> {
if s.is_empty() {
Ok(FloatOrString(0.0))
} else {
let s = s.replace(',', "");
match s.parse::<f64>() {
Ok(num) => Ok(FloatOrString(num)),
_ => Ok(FloatOrString(std::f64::NAN)),
}
}
}
}
impl From<FloatOrString> for f64 {
fn from(fos: FloatOrString) -> f64 {
fos.0
}
}
impl From<u64> for FloatOrString {
fn from(val: u64) -> FloatOrString {
FloatOrString(val as f64)
}
}
impl From<i64> for FloatOrString {
fn from(val: i64) -> FloatOrString {
FloatOrString(val as f64)
}
}
impl From<f64> for FloatOrString {
fn from(val: f64) -> FloatOrString {
FloatOrString(val)
}
}
impl fmt::Display for FloatOrString {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl default::Default for FloatOrString {
fn default() -> FloatOrString {
FloatOrString(std::f64::NAN)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Deserialize, Debug)]
struct DoubleNum {
x: FloatOrString,
y: FloatOrString,
z: FloatOrString,
}
#[derive(Deserialize, Debug)]
struct DoubleNumOpt {
pub x: Option<FloatOrString>,
}
#[test]
fn convert_str_num() {
let str_num = "2".to_string();
let num: f64 = str_num.parse().unwrap();
assert_eq!(num, 2.0);
let json: serde_json::Value =
serde_json::from_str("{\"x\":\"2.1\",\"y\":3,\"z\":3.4}").unwrap();
let d_num: DoubleNum = serde_json::from_value(json).unwrap();
assert_eq!(d_num.x.0, 2.1);
assert_eq!(d_num.y.0, 3.0);
assert_eq!(d_num.z.0, 3.4);
}
#[test]
fn convert_null_str_num() {
let json: serde_json::Value =
serde_json::from_str("{\"x\":null, \"y\":1, \"z\":null}").unwrap();
let d_num: DoubleNumOpt = serde_json::from_value(json).unwrap();
assert!(d_num.x.is_none());
}
#[test]
fn convert_vec_on_str_num() {
let json: serde_json::Value = serde_json::from_str("[\"2.1\",3,3.4]").unwrap();
let v: Vec<FloatOrString> = serde_json::from_value(json).unwrap();
assert_eq!(v[0].0, 2.1);
assert_eq!(v[1].0, 3.0);
assert_eq!(v[2].0, 3.4);
}
#[test]
fn print_str_num() {
let str_num = FloatOrString { 0: 2.3 };
let num_as_str = format!("{}", str_num);
assert_eq!(num_as_str, "2.3");
}
#[test]
fn float_string_to_f64() {
let str_num = FloatOrString { 0: 2.3 };
let num: f64 = str_num.into();
assert_eq!(num, 2.3);
}
}