Skip to main content

pnc_engine/
value.rs

1use num_bigint::BigInt;
2use num_rational::BigRational;
3use serde::{Deserialize, Deserializer, Serialize, Serializer};
4use std::fmt;
5
6#[derive(Debug, Clone, PartialEq, Eq)]
7pub enum Value {
8    Int(BigInt),
9    Rational(BigRational),
10    Unit { number: Box<Value>, unit: UnitId },
11}
12
13// UnitId is defined in unit.rs; re-export for convenience.
14pub use crate::unit::UnitId;
15
16impl Serialize for Value {
17    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
18    where
19        S: Serializer,
20    {
21        use serde::ser::SerializeMap;
22        match self {
23            Value::Int(n) => {
24                let mut map = serializer.serialize_map(Some(2))?;
25                map.serialize_entry("kind", "int")?;
26                map.serialize_entry("value", &n.to_string())?;
27                map.end()
28            }
29            Value::Rational(r) => {
30                let mut map = serializer.serialize_map(Some(3))?;
31                map.serialize_entry("kind", "rational")?;
32                map.serialize_entry("numerator", &r.numer().to_string())?;
33                map.serialize_entry("denominator", &r.denom().to_string())?;
34                map.end()
35            }
36            Value::Unit { number, unit } => {
37                let (number_kind, numerator, denominator) = match number.as_ref() {
38                    Value::Int(n) => ("int", n.to_string(), "1".to_string()),
39                    Value::Rational(r) => {
40                        ("rational", r.numer().to_string(), r.denom().to_string())
41                    }
42                    Value::Unit { .. } => {
43                        return Err(serde::ser::Error::custom("unit may not wrap another unit"))
44                    }
45                };
46                let mut map = serializer.serialize_map(Some(5))?;
47                map.serialize_entry("kind", "unit")?;
48                map.serialize_entry("number_kind", number_kind)?;
49                map.serialize_entry("numerator", &numerator)?;
50                map.serialize_entry("denominator", &denominator)?;
51                map.serialize_entry("unit", &unit.to_string())?;
52                map.end()
53            }
54        }
55    }
56}
57
58impl<'de> Deserialize<'de> for Value {
59    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
60    where
61        D: Deserializer<'de>,
62    {
63        #[derive(Deserialize)]
64        struct Raw {
65            kind: String,
66            value: Option<String>,
67            numerator: Option<String>,
68            denominator: Option<String>,
69            number_kind: Option<String>,
70            unit: Option<String>,
71        }
72
73        let raw = Raw::deserialize(deserializer)?;
74        match raw.kind.as_str() {
75            "int" => {
76                let value = raw
77                    .value
78                    .ok_or_else(|| serde::de::Error::missing_field("value"))?;
79                let n: BigInt = value
80                    .parse()
81                    .map_err(|_| serde::de::Error::custom("invalid integer"))?;
82                Ok(Value::Int(n))
83            }
84            "rational" => {
85                let num_str = raw
86                    .numerator
87                    .ok_or_else(|| serde::de::Error::missing_field("numerator"))?;
88                let den_str = raw
89                    .denominator
90                    .ok_or_else(|| serde::de::Error::missing_field("denominator"))?;
91                let n: BigInt = num_str
92                    .parse()
93                    .map_err(|_| serde::de::Error::custom("invalid numerator"))?;
94                let d: BigInt = den_str
95                    .parse()
96                    .map_err(|_| serde::de::Error::custom("invalid denominator"))?;
97                if d == BigInt::from(0) {
98                    return Err(serde::de::Error::custom("zero denominator"));
99                }
100                Ok(Value::Rational(BigRational::new(n, d)))
101            }
102            "unit" => {
103                let number_kind = raw
104                    .number_kind
105                    .ok_or_else(|| serde::de::Error::missing_field("number_kind"))?;
106                let num_str = raw
107                    .numerator
108                    .ok_or_else(|| serde::de::Error::missing_field("numerator"))?;
109                let den_str = raw
110                    .denominator
111                    .ok_or_else(|| serde::de::Error::missing_field("denominator"))?;
112                let unit_str = raw
113                    .unit
114                    .ok_or_else(|| serde::de::Error::missing_field("unit"))?;
115
116                let number = match number_kind.as_str() {
117                    "int" => {
118                        let n: BigInt = num_str
119                            .parse()
120                            .map_err(|_| serde::de::Error::custom("invalid integer"))?;
121                        Value::Int(n)
122                    }
123                    "rational" => {
124                        let n: BigInt = num_str
125                            .parse()
126                            .map_err(|_| serde::de::Error::custom("invalid numerator"))?;
127                        let d: BigInt = den_str
128                            .parse()
129                            .map_err(|_| serde::de::Error::custom("invalid denominator"))?;
130                        if d == BigInt::from(0) {
131                            return Err(serde::de::Error::custom("zero denominator"));
132                        }
133                        Value::Rational(BigRational::new(n, d))
134                    }
135                    _ => return Err(serde::de::Error::custom("unknown number_kind")),
136                };
137                let unit = match unit_str.as_str() {
138                    "c" => UnitId::Celsius,
139                    "f" => UnitId::Fahrenheit,
140                    "k" => UnitId::Kelvin,
141                    other => UnitId::Unknown(other.to_string()),
142                };
143                Ok(Value::Unit {
144                    number: Box::new(number),
145                    unit,
146                })
147            }
148            other => Err(serde::de::Error::custom(format!("unknown kind: {}", other))),
149        }
150    }
151}
152
153impl fmt::Display for Value {
154    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155        match self {
156            Value::Int(n) => write!(f, "{}", n),
157            Value::Rational(r) => {
158                if r.denom() == &BigInt::from(1) {
159                    write!(f, "{}", r.numer())
160                } else {
161                    write!(f, "{}/{}", r.numer(), r.denom())
162                }
163            }
164            Value::Unit { number, unit } => write!(f, "{} {}", number, unit),
165        }
166    }
167}