oca_bundle/state/oca/overlay/
unit.rs

1use crate::state::{attribute::Attribute, oca::Overlay};
2use oca_ast::ast::OverlayType;
3use said::{sad::SerializationFormats, sad::SAD};
4use serde::{ser::SerializeMap, Deserialize, Serialize, Serializer};
5use std::any::Any;
6use std::collections::HashMap;
7
8#[derive(Serialize, Deserialize, Debug, Clone, Eq, Hash, PartialEq)]
9#[serde(rename_all = "lowercase")]
10pub enum MeasurementSystem {
11    Metric,
12    Imperial,
13}
14
15pub struct AttributeUnit {
16    pub measurement_system: MeasurementSystem,
17    pub unit: MeasurementUnit,
18}
19#[derive(Eq, Hash, PartialEq, Debug, Clone, Serialize, Deserialize)]
20#[serde(untagged)]
21pub enum MeasurementUnit {
22    Metric(MetricUnit),
23    Imperial(ImperialUnit),
24}
25
26#[derive(Eq, Hash, Serialize, Deserialize, Debug, Clone, PartialEq)]
27#[serde(rename_all = "lowercase")]
28pub enum MetricUnit {
29    Kilogram,
30    Gram,
31    Milligram,
32    Liter,
33    Milliliter,
34    Meter,
35    Centimeter,
36    Millimeter,
37    Inch,
38    Foot,
39    Yard,
40    Mile,
41    Celsius,
42    Fahrenheit,
43    Kelvin,
44    Percent,
45    Count,
46    Other,
47}
48
49#[derive(Eq, Hash, Serialize, Deserialize, Debug, Clone, PartialEq)]
50#[serde(rename_all = "lowercase")]
51pub enum ImperialUnit {
52    Pound,
53    Ounce,
54    Gallon,
55    Quart,
56    Pint,
57    FluidOunce,
58    Inch,
59    Foot,
60    Yard,
61    Mile,
62    Celsius,
63    Fahrenheit,
64    Kelvin,
65    Percent,
66    Count,
67    Other,
68}
69
70impl std::str::FromStr for MetricUnit {
71    type Err = ();
72
73    fn from_str(s: &str) -> Result<Self, Self::Err> {
74        match s {
75            "kilogram" => Ok(MetricUnit::Kilogram),
76            "kg" => Ok(MetricUnit::Kilogram),
77            "gram" => Ok(MetricUnit::Gram),
78            "g" => Ok(MetricUnit::Gram),
79            "milligram" => Ok(MetricUnit::Milligram),
80            "mg" => Ok(MetricUnit::Milligram),
81            "liter" => Ok(MetricUnit::Liter),
82            "l" => Ok(MetricUnit::Liter),
83            "meter" => Ok(MetricUnit::Meter),
84            "m" => Ok(MetricUnit::Meter),
85            "milliliter" => Ok(MetricUnit::Milliliter),
86            "ml" => Ok(MetricUnit::Milliliter),
87            "centimeter" => Ok(MetricUnit::Centimeter),
88            "cm" => Ok(MetricUnit::Centimeter),
89            "millimeter" => Ok(MetricUnit::Millimeter),
90            "mm" => Ok(MetricUnit::Millimeter),
91            "inch" => Ok(MetricUnit::Inch),
92            "in" => Ok(MetricUnit::Inch),
93            "foot" => Ok(MetricUnit::Foot),
94            "ft" => Ok(MetricUnit::Foot),
95            "yard" => Ok(MetricUnit::Yard),
96            "yd" => Ok(MetricUnit::Yard),
97            "mile" => Ok(MetricUnit::Mile),
98            "mi" => Ok(MetricUnit::Mile),
99            "celsius" => Ok(MetricUnit::Celsius),
100            "c" => Ok(MetricUnit::Celsius),
101            "fahrenheit" => Ok(MetricUnit::Fahrenheit),
102            "f" => Ok(MetricUnit::Fahrenheit),
103            "kelvin" => Ok(MetricUnit::Kelvin),
104            "k" => Ok(MetricUnit::Kelvin),
105            "percent" => Ok(MetricUnit::Percent),
106            "%" => Ok(MetricUnit::Percent),
107            "count" => Ok(MetricUnit::Count),
108            "other" => Ok(MetricUnit::Other),
109            _ => Err(()),
110        }
111    }
112}
113
114impl std::str::FromStr for ImperialUnit {
115    type Err = ();
116
117    fn from_str(s: &str) -> Result<Self, Self::Err> {
118        match s {
119            "pound" => Ok(ImperialUnit::Pound),
120            "lb" => Ok(ImperialUnit::Pound),
121            "ounce" => Ok(ImperialUnit::Ounce),
122            "oz" => Ok(ImperialUnit::Ounce),
123            "gallon" => Ok(ImperialUnit::Gallon),
124            "gal" => Ok(ImperialUnit::Gallon),
125            "quart" => Ok(ImperialUnit::Quart),
126            "qt" => Ok(ImperialUnit::Quart),
127            "pint" => Ok(ImperialUnit::Pint),
128            "pt" => Ok(ImperialUnit::Pint),
129            "fluid ounce" => Ok(ImperialUnit::FluidOunce),
130            "fl oz" => Ok(ImperialUnit::FluidOunce),
131            "inch" => Ok(ImperialUnit::Inch),
132            "in" => Ok(ImperialUnit::Inch),
133            "foot" => Ok(ImperialUnit::Foot),
134            "ft" => Ok(ImperialUnit::Foot),
135            "yard" => Ok(ImperialUnit::Yard),
136            "yd" => Ok(ImperialUnit::Yard),
137            "mile" => Ok(ImperialUnit::Mile),
138            "mi" => Ok(ImperialUnit::Mile),
139            "celsius" => Ok(ImperialUnit::Celsius),
140            "c" => Ok(ImperialUnit::Celsius),
141            "fahrenheit" => Ok(ImperialUnit::Fahrenheit),
142            "f" => Ok(ImperialUnit::Fahrenheit),
143            "kelvin" => Ok(ImperialUnit::Kelvin),
144            "k" => Ok(ImperialUnit::Kelvin),
145            "percent" => Ok(ImperialUnit::Percent),
146            "%" => Ok(ImperialUnit::Percent),
147            "count" => Ok(ImperialUnit::Count),
148            "other" => Ok(ImperialUnit::Other),
149            _ => Err(()),
150        }
151    }
152}
153
154impl std::str::FromStr for MeasurementSystem {
155    type Err = ();
156
157    fn from_str(s: &str) -> Result<Self, Self::Err> {
158        match s {
159            "metric" => Ok(MeasurementSystem::Metric),
160            "si" => Ok(MeasurementSystem::Metric),
161            "imperial" => Ok(MeasurementSystem::Imperial),
162            "iu" => Ok(MeasurementSystem::Imperial),
163            _ => Err(()),
164        }
165    }
166}
167
168pub trait Unit {
169    fn set_unit(&mut self, attr_unit: AttributeUnit);
170}
171
172impl Unit for Attribute {
173    fn set_unit(&mut self, attr_unit: AttributeUnit) {
174        match self.units {
175            Some(ref mut units) => {
176                units.insert(attr_unit.measurement_system, attr_unit.unit);
177            }
178            None => {
179                let mut units = HashMap::new();
180                units.insert(attr_unit.measurement_system, attr_unit.unit);
181                self.units = Some(units);
182            }
183        }
184    }
185}
186
187pub fn serialize_attributes<S>(
188    attributes: &HashMap<String, MeasurementUnit>,
189    s: S,
190) -> Result<S::Ok, S::Error>
191where
192    S: Serializer,
193{
194    use std::collections::BTreeMap;
195
196    let mut ser = s.serialize_map(Some(attributes.len()))?;
197    let sorted_attributes: BTreeMap<_, _> = attributes.iter().collect();
198    for (k, v) in sorted_attributes {
199        ser.serialize_entry(k, v)?;
200    }
201    ser.end()
202}
203
204#[derive(SAD, Serialize, Deserialize, Debug, Clone)]
205pub struct UnitOverlay {
206    #[said]
207    #[serde(rename = "d")]
208    said: Option<said::SelfAddressingIdentifier>,
209    #[serde(rename = "type")]
210    overlay_type: OverlayType,
211    capture_base: Option<said::SelfAddressingIdentifier>,
212    pub measurement_system: MeasurementSystem,
213    #[serde(serialize_with = "serialize_attributes")]
214    pub attribute_units: HashMap<String, MeasurementUnit>,
215}
216
217impl Overlay for UnitOverlay {
218    fn as_any(&self) -> &dyn Any {
219        self
220    }
221    fn capture_base(&self) -> &Option<said::SelfAddressingIdentifier> {
222        &self.capture_base
223    }
224    fn set_capture_base(&mut self, said: &said::SelfAddressingIdentifier) {
225        self.capture_base = Some(said.clone());
226    }
227    fn said(&self) -> &Option<said::SelfAddressingIdentifier> {
228        &self.said
229    }
230    fn overlay_type(&self) -> &OverlayType {
231        &self.overlay_type
232    }
233
234    fn attributes(&self) -> Vec<&String> {
235        self.attribute_units.keys().collect::<Vec<&String>>()
236    }
237
238    fn add(&mut self, attribute: &Attribute) {
239        if let Some(units) = &attribute.units {
240            if let Some(unit) = units.get(&self.measurement_system) {
241                self.attribute_units
242                    .insert(attribute.name.clone(), unit.clone());
243            }
244        }
245    }
246}
247impl UnitOverlay {
248    pub fn new(measurement_system: MeasurementSystem) -> UnitOverlay {
249        UnitOverlay {
250            capture_base: None,
251            said: None,
252            overlay_type: OverlayType::Unit,
253            measurement_system,
254            attribute_units: HashMap::new(),
255        }
256    }
257
258    pub fn measurement_system(&self) -> Option<&MeasurementSystem> {
259        Some(&self.measurement_system)
260    }
261}
262
263#[cfg(test)]
264mod tests {
265
266    use super::*;
267    use crate::state::attribute::Attribute;
268    use crate::state::oca::overlay::unit::MeasurementSystem;
269
270    #[test]
271    fn test_set_unit() {
272        let attribute = cascade! {
273            Attribute::new("test".to_string());
274            ..set_unit(AttributeUnit { measurement_system: MeasurementSystem::Metric, unit: MeasurementUnit::Metric(MetricUnit::Kilogram)});
275            ..set_unit(AttributeUnit { measurement_system: MeasurementSystem::Imperial, unit: MeasurementUnit::Imperial(ImperialUnit::Pound) });
276        };
277
278        // assert eq units
279        assert_eq!(
280            attribute.units,
281            Some({
282                let mut units = HashMap::new();
283                units.insert(
284                    MeasurementSystem::Metric,
285                    MeasurementUnit::Metric(MetricUnit::Kilogram),
286                );
287                units.insert(
288                    MeasurementSystem::Imperial,
289                    MeasurementUnit::Imperial(ImperialUnit::Pound),
290                );
291                units
292            })
293        );
294    }
295}