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!(
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}