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