proxmox-api 0.2.0

Rust bindings for the Proxmox VE HTTP API
Documentation
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BoundedIntegerError {
    ValueLower,
    ValueHigher,
}

impl std::fmt::Display for BoundedIntegerError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            BoundedIntegerError::ValueLower => write!(f, "value is below minimum"),
            BoundedIntegerError::ValueHigher => write!(f, "value is above maximum"),
        }
    }
}

impl std::error::Error for BoundedIntegerError {}

pub trait BoundedInteger {
    const MIN: Option<i128> = None;
    const MAX: Option<i128> = None;
    const DEFAULT: Option<i128> = None;
    const TYPE_DESCRIPTION: &'static str;

    fn get(&self) -> i128;

    fn new(value: i128) -> Result<Self, BoundedIntegerError>
    where
        Self: Sized;

    fn validate(value: i128) -> Result<(), BoundedIntegerError> {
        if let Some(min) = Self::MIN
            && value < min
        {
            return Err(BoundedIntegerError::ValueLower);
        }
        if let Some(max) = Self::MAX
            && value > max
        {
            return Err(BoundedIntegerError::ValueHigher);
        }
        Ok(())
    }
}

use serde::{Deserialize, Deserializer, Serializer};

pub fn serialize_bounded_integer<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
    T: BoundedInteger,
{
    serializer.serialize_i128(value.get())
}

fn parse_value_to_i128(value: &serde_json::Value) -> Option<i128> {
    match value {
        serde_json::Value::Number(n) => n.as_i128(),
        serde_json::Value::String(s) => s.parse::<i128>().ok(),
        _ => None,
    }
}

pub fn deserialize_bounded_integer<'de, T, D>(deserializer: D) -> Result<T, D::Error>
where
    D: Deserializer<'de>,
    T: BoundedInteger + TryFrom<i128, Error = BoundedIntegerError>,
{
    let value = Option::<serde_json::Value>::deserialize(deserializer)?;
    let i128_value = value
        .as_ref()
        .and_then(parse_value_to_i128)
        .ok_or_else(|| serde::de::Error::custom("could not parse value as i128"))?;
    T::try_from(i128_value).map_err(|e| {
        serde::de::Error::custom(format!(
            "could not parse as {} with error: {}",
            T::TYPE_DESCRIPTION,
            e
        ))
    })
}