proxmox_api/types/
bounded_number.rs1#[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}