Skip to main content

proxmox_api/types/
bounded_number.rs

1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2pub enum BoundedNumberError {
3    ValueLower,
4    ValueHigher,
5    NotANumber,
6}
7
8impl std::fmt::Display for BoundedNumberError {
9    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
10        match self {
11            BoundedNumberError::ValueLower => write!(f, "value is below minimum"),
12            BoundedNumberError::ValueHigher => write!(f, "value is above maximum"),
13            BoundedNumberError::NotANumber => write!(f, "value is not a number (NaN)"),
14        }
15    }
16}
17
18impl std::error::Error for BoundedNumberError {}
19
20pub trait BoundedNumber {
21    const MIN: Option<f64> = None;
22    const MAX: Option<f64> = None;
23    const DEFAULT: Option<f64> = None;
24    const TYPE_DESCRIPTION: &'static str;
25
26    fn get(&self) -> f64;
27
28    fn new(value: f64) -> Result<Self, BoundedNumberError>
29    where
30        Self: Sized;
31
32    fn validate(value: f64) -> Result<(), BoundedNumberError> {
33        if value.is_nan() {
34            return Err(BoundedNumberError::NotANumber);
35        }
36        if let Some(min) = Self::MIN
37            && value < min
38        {
39            return Err(BoundedNumberError::ValueLower);
40        }
41        if let Some(max) = Self::MAX
42            && value > max
43        {
44            return Err(BoundedNumberError::ValueHigher);
45        }
46        Ok(())
47    }
48}
49
50use serde::{Deserialize, Deserializer, Serializer};
51
52pub fn serialize_bounded_number<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
53where
54    S: Serializer,
55    T: BoundedNumber,
56{
57    serializer.serialize_f64(value.get())
58}
59
60fn parse_value_to_f64(value: &serde_json::Value) -> Option<f64> {
61    match value {
62        serde_json::Value::Number(n) => n.as_f64(),
63        serde_json::Value::String(s) => s.parse::<f64>().ok(),
64        _ => None,
65    }
66}
67
68pub fn deserialize_bounded_number<'de, T, D>(deserializer: D) -> Result<T, D::Error>
69where
70    D: Deserializer<'de>,
71    T: BoundedNumber + TryFrom<f64, Error = BoundedNumberError>,
72{
73    let value = Option::<serde_json::Value>::deserialize(deserializer)?;
74    let f64_value = value
75        .as_ref()
76        .and_then(parse_value_to_f64)
77        .ok_or_else(|| serde::de::Error::custom("could not parse value as f64"))?;
78    T::try_from(f64_value).map_err(|e| {
79        serde::de::Error::custom(format!(
80            "could not parse as {} with error: {}",
81            T::TYPE_DESCRIPTION,
82            e
83        ))
84    })
85}