plugx-input 0.3.1

simple and flexible data-structure with diff, merge, and validation.
Documentation
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::fmt::{Display, Formatter};

#[derive(Clone, Debug, Copy, Deserialize, Serialize)]
#[serde(untagged, deny_unknown_fields)]
pub enum InputSchemaTypeNumberValue {
    Integer(isize),
    Float(f64),
}

impl Display for InputSchemaTypeNumberValue {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.write_str(
            match self {
                Self::Integer(i) => i.to_string(),
                Self::Float(f) => f.to_string(),
            }
            .as_str(),
        )
    }
}

impl InputSchemaTypeNumberValue {
    pub fn from_integer<I: Into<isize>>(i: I) -> Self {
        Self::Integer(i.into())
    }

    pub fn from_float<F: Into<f64>>(f: F) -> Self {
        Self::Float(f.into())
    }

    pub fn round(&self) -> Self {
        match self {
            Self::Float(f) => Self::Integer(f.round() as isize),
            Self::Integer(i) => Self::Integer(*i),
        }
    }

    pub fn round_mut(&mut self) {
        *self = match self {
            Self::Float(f) => Self::Integer(f.round() as isize),
            Self::Integer(i) => Self::Integer(*i),
        };
    }

    pub fn trunc(&self) -> Self {
        match self {
            Self::Float(f) => Self::Integer(f.trunc() as isize),
            Self::Integer(i) => Self::Integer(*i),
        }
    }

    pub fn trunc_mut(&mut self) {
        *self = match self {
            Self::Float(f) => Self::Integer(f.trunc() as isize),
            Self::Integer(i) => Self::Integer(*i),
        };
    }

    pub fn integer(&self) -> Option<isize> {
        if let Self::Integer(i) = self {
            Some(*i)
        } else {
            None
        }
    }

    pub fn float(&self) -> f64 {
        match self {
            Self::Float(f) => *f,
            Self::Integer(i) => *i as f64,
        }
    }
}

impl PartialEq for InputSchemaTypeNumberValue {
    fn eq(&self, other: &Self) -> bool {
        self.float() == other.float()
    }
}

impl PartialOrd for InputSchemaTypeNumberValue {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        self.float().partial_cmp(&other.float())
    }
}

impl From<InputSchemaTypeNumberValue> for f64 {
    fn from(value: InputSchemaTypeNumberValue) -> Self {
        value.float()
    }
}

impl From<i8> for InputSchemaTypeNumberValue {
    fn from(i: i8) -> Self {
        Self::Integer(i as isize)
    }
}

impl From<i16> for InputSchemaTypeNumberValue {
    fn from(i: i16) -> Self {
        Self::Integer(i as isize)
    }
}
impl From<i32> for InputSchemaTypeNumberValue {
    fn from(i: i32) -> Self {
        Self::Integer(i as isize)
    }
}

impl From<u8> for InputSchemaTypeNumberValue {
    fn from(u: u8) -> Self {
        Self::Integer(u as isize)
    }
}

impl From<u16> for InputSchemaTypeNumberValue {
    fn from(u: u16) -> Self {
        Self::Integer(u as isize)
    }
}
impl From<u32> for InputSchemaTypeNumberValue {
    fn from(u: u32) -> Self {
        Self::Integer(u as isize)
    }
}

impl From<isize> for InputSchemaTypeNumberValue {
    fn from(i: isize) -> Self {
        Self::Integer(i)
    }
}

impl From<f32> for InputSchemaTypeNumberValue {
    fn from(f: f32) -> Self {
        Self::Float(f as f64)
    }
}

impl From<f64> for InputSchemaTypeNumberValue {
    fn from(f: f64) -> Self {
        Self::Float(f)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde_json::json;

    #[test]
    fn from() {
        assert_eq!(
            InputSchemaTypeNumberValue::from(3.14f64),
            InputSchemaTypeNumberValue::from_float(3.14)
        );
        assert_eq!(
            InputSchemaTypeNumberValue::from(3i8),
            InputSchemaTypeNumberValue::from_float(3.0)
        );
        assert_eq!(
            InputSchemaTypeNumberValue::from(-10i16),
            InputSchemaTypeNumberValue::from_float(-10.0)
        );
    }

    #[test]
    fn serde() {
        let json = serde_json::to_string_pretty(&json!(3.6)).unwrap();
        let mut n: InputSchemaTypeNumberValue = serde_json::from_str(json.as_str()).unwrap();
        assert_eq!(n.float(), 3.6);
        assert_eq!(n.round(), 4.into());
        assert_eq!(n.trunc(), 3.into());
        assert_eq!(n.integer(), None);
        n.trunc_mut();
        assert_eq!(n.integer(), Some(3isize.into()));
    }
}