style/typed_om/sum_value.rs
1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! Typed OM Sum Value.
6
7use crate::typed_om::numeric_values::NoCalcNumeric;
8use crate::typed_om::{NumericValue, UnitValue};
9use std::collections::HashMap;
10use style_traits::CssString;
11
12type UnitMap = HashMap<String, i32>;
13
14/// <https://drafts.css-houdini.org/css-typed-om-1/#cssnumericvalue-sum-value>
15#[derive(Clone, Debug)]
16struct SumValueItem {
17 value: f32,
18 unit_map: UnitMap,
19}
20
21impl SumValueItem {
22 /// <https://drafts.css-houdini.org/css-typed-om-1/#create-a-cssunitvalue-from-a-sum-value-item>
23 fn to_unit_value(&self) -> Result<UnitValue, ()> {
24 // Step 1.
25 if self.unit_map.len() > 1 {
26 return Err(());
27 }
28
29 // Step 2.
30 if self.unit_map.is_empty() {
31 return Ok(UnitValue {
32 value: self.value,
33 unit: CssString::from("number"),
34 });
35 }
36
37 // Step 3.
38 let (unit, power) = self.unit_map.iter().next().unwrap();
39 if *power != 1 {
40 return Err(());
41 }
42
43 // Step 4.
44 Ok(UnitValue {
45 value: self.value,
46 unit: CssString::from(unit),
47 })
48 }
49}
50
51/// <https://drafts.css-houdini.org/css-typed-om-1/#cssnumericvalue-sum-value>
52#[derive(Clone, Debug)]
53pub struct SumValue(Vec<SumValueItem>);
54
55impl SumValue {
56 /// <https://drafts.css-houdini.org/css-typed-om-1/#create-a-sum-value>
57 pub fn try_from_numeric_value(value: &NumericValue) -> Result<Self, ()> {
58 match value {
59 // CSSUnitValue
60 NumericValue::Unit(unit_value) => {
61 // Step 1.
62 let mut value = unit_value.value;
63 let mut unit = unit_value.unit.to_string();
64
65 // Step2.
66 let numeric = NoCalcNumeric::parse_unit_value(value, unit.as_str())?;
67 if let Some(canonical_unit) = numeric.canonical_unit() {
68 let canonical = numeric.to(canonical_unit)?;
69 value = canonical.unitless_value();
70 unit = canonical.unit().to_string();
71 }
72
73 // Step 3.
74 if unit.eq_ignore_ascii_case("number") {
75 return Ok(Self(vec![SumValueItem {
76 value,
77 unit_map: UnitMap::new(),
78 }]));
79 }
80
81 // Step 4.
82 Ok(Self(vec![SumValueItem {
83 value,
84 unit_map: [(unit, 1)].into_iter().collect::<UnitMap>(),
85 }]))
86 },
87
88 // CSSMathSum
89 NumericValue::Sum(math_sum) => {
90 // Step 1.
91 let mut values: Vec<SumValueItem> = Vec::new();
92
93 // Step 2.
94 for item in &math_sum.values {
95 // Step 2.1.
96 let value = SumValue::try_from_numeric_value(item)?;
97
98 // Step 2.2.
99 for sub_value in value.0 {
100 // Step 2.2.1.
101 if let Some(item) = values
102 .iter_mut()
103 .find(|item| item.unit_map == sub_value.unit_map)
104 {
105 item.value += sub_value.value;
106 continue;
107 }
108
109 // Step 2.2.2.
110 values.push(sub_value);
111 }
112 }
113
114 // Step 3.
115
116 // TODO: Create a type [1] from the unit map of each item of
117 // values, and add [2] all the types together. If the result is
118 // failure, return failure.
119 //
120 // [1] https://drafts.css-houdini.org/css-typed-om-1/#create-a-type-from-a-unit-map
121 // [2] https://drafts.css-houdini.org/css-typed-om-1/#cssnumericvalue-add-two-types
122
123 // Step 4.
124 Ok(SumValue(values))
125 },
126 }
127 }
128
129 /// Step 3 of:
130 /// https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-to
131 pub fn resolve_to_unit(&self, unit: &str) -> Result<UnitValue, ()> {
132 if self.0.len() != 1 {
133 return Err(());
134 }
135
136 let sole_item = &self.0[0];
137
138 let item = sole_item.to_unit_value()?;
139
140 let item = {
141 let numeric =
142 NoCalcNumeric::parse_unit_value(item.value, item.unit.to_string().as_str())?;
143 let converted = numeric.to(unit)?;
144
145 UnitValue {
146 value: converted.unitless_value(),
147 unit: CssString::from(converted.unit()),
148 }
149 };
150
151 Ok(item)
152 }
153}