use crate::typed_om::numeric_values::NoCalcNumeric;
use crate::typed_om::{NumericValue, UnitValue};
use std::collections::HashMap;
use style_traits::CssString;
type UnitMap = HashMap<String, i32>;
#[derive(Clone, Debug)]
struct SumValueItem {
value: f32,
unit_map: UnitMap,
}
impl SumValueItem {
fn to_unit_value(&self) -> Result<UnitValue, ()> {
if self.unit_map.len() > 1 {
return Err(());
}
if self.unit_map.is_empty() {
return Ok(UnitValue {
value: self.value,
unit: CssString::from("number"),
});
}
let (unit, power) = self.unit_map.iter().next().unwrap();
if *power != 1 {
return Err(());
}
Ok(UnitValue {
value: self.value,
unit: CssString::from(unit),
})
}
}
#[derive(Clone, Debug)]
pub struct SumValue(Vec<SumValueItem>);
impl SumValue {
pub fn try_from_numeric_value(value: &NumericValue) -> Result<Self, ()> {
match value {
NumericValue::Unit(unit_value) => {
let mut value = unit_value.value;
let mut unit = unit_value.unit.to_string();
let numeric = NoCalcNumeric::parse_unit_value(value, unit.as_str())?;
if let Some(canonical_unit) = numeric.canonical_unit() {
let canonical = numeric.to(canonical_unit)?;
value = canonical.unitless_value();
unit = canonical.unit().to_string();
}
if unit.eq_ignore_ascii_case("number") {
return Ok(Self(vec![SumValueItem {
value,
unit_map: UnitMap::new(),
}]));
}
Ok(Self(vec![SumValueItem {
value,
unit_map: [(unit, 1)].into_iter().collect::<UnitMap>(),
}]))
},
NumericValue::Sum(math_sum) => {
let mut values: Vec<SumValueItem> = Vec::new();
for item in &math_sum.values {
let value = SumValue::try_from_numeric_value(item)?;
for sub_value in value.0 {
if let Some(item) = values
.iter_mut()
.find(|item| item.unit_map == sub_value.unit_map)
{
item.value += sub_value.value;
continue;
}
values.push(sub_value);
}
}
Ok(SumValue(values))
},
}
}
pub fn resolve_to_unit(&self, unit: &str) -> Result<UnitValue, ()> {
if self.0.len() != 1 {
return Err(());
}
let sole_item = &self.0[0];
let item = sole_item.to_unit_value()?;
let item = {
let numeric =
NoCalcNumeric::parse_unit_value(item.value, item.unit.to_string().as_str())?;
let converted = numeric.to(unit)?;
UnitValue {
value: converted.unitless_value(),
unit: CssString::from(converted.unit()),
}
};
Ok(item)
}
}