1use {
5 super::{
6 conversions::{
7 FontRelativeLengthConversion,
8 PercentageConversion,
9 ViewportPercentageLengthConversion,
10 },
11 PercentageUnit,
12 Unit,
13 },
14 crate::{
15 domain::{
16 expressions::{
17 CalcExpression,
18 CalculablePropertyValue::{self, *},
19 FunctionParser,
20 },
21 numbers::{CssNumber, CssNumberNewType},
22 },
23 parsers::ParserContext,
24 CustomParseError,
25 },
26 cssparser::{ParseError, Parser, ParserInput, ToCss, Token},
27 either::{Either, Left},
28 std::{fmt, ops::*},
29 NumberOrPercentageUnit::*,
30};
31
32#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
33pub enum NumberOrPercentageUnit<Number: CssNumber> {
34 IsNumber(Number),
35 IsPercentage(PercentageUnit<Number>),
36}
37
38impl<Number: CssNumber> ToCss for NumberOrPercentageUnit<Number> {
39 fn to_css<W: fmt::Write>(&self, dest: &mut W) -> fmt::Result {
40 use self::NumberOrPercentageUnit::*;
41 match *self {
42 IsNumber(ref length) => length.to_css(dest),
43 IsPercentage(ref length) => length.to_css(dest),
44 }
45 }
46}
47
48impl<Number: CssNumber> Default for NumberOrPercentageUnit<Number> {
49 #[inline(always)]
50 fn default() -> Self {
51 NumberOrPercentageUnit::IsNumber(Number::default())
52 }
53}
54
55impl<Number: CssNumber> Add<Number> for NumberOrPercentageUnit<Number> {
56 type Output = Self;
57
58 #[inline(always)]
59 fn add(self, rhs: Number) -> Self::Output {
60 use self::NumberOrPercentageUnit::*;
61 match self {
62 IsNumber(length) => IsNumber(length + rhs),
63 IsPercentage(length) => IsPercentage(length + rhs),
64 }
65 }
66}
67
68impl<Number: CssNumber> AddAssign<Number> for NumberOrPercentageUnit<Number> {
69 #[inline(always)]
70 fn add_assign(&mut self, rhs: Number) {
71 use self::NumberOrPercentageUnit::*;
72 match *self {
73 IsNumber(ref mut length) => *length = *length + rhs,
74 IsPercentage(ref mut length) => *length = *length + rhs,
75 }
76 }
77}
78
79impl<Number: CssNumber> Sub<Number> for NumberOrPercentageUnit<Number> {
80 type Output = Self;
81
82 #[inline(always)]
83 fn sub(self, rhs: Number) -> Self::Output {
84 use self::NumberOrPercentageUnit::*;
85 match self {
86 IsNumber(length) => IsNumber(length - rhs),
87 IsPercentage(length) => IsPercentage(length - rhs),
88 }
89 }
90}
91
92impl<Number: CssNumber> SubAssign<Number> for NumberOrPercentageUnit<Number> {
93 #[inline(always)]
94 fn sub_assign(&mut self, rhs: Number) {
95 use self::NumberOrPercentageUnit::*;
96 match *self {
97 IsNumber(ref mut length) => *length = *length - rhs,
98 IsPercentage(ref mut length) => *length = *length - rhs,
99 }
100 }
101}
102
103impl<Number: CssNumber> Mul<Number> for NumberOrPercentageUnit<Number> {
104 type Output = Self;
105
106 #[inline(always)]
107 fn mul(self, rhs: Number) -> Self::Output {
108 use self::NumberOrPercentageUnit::*;
109 match self {
110 IsNumber(length) => IsNumber(length * rhs),
111 IsPercentage(length) => IsPercentage(length * rhs),
112 }
113 }
114}
115
116impl<Number: CssNumber> MulAssign<Number> for NumberOrPercentageUnit<Number> {
117 #[inline(always)]
118 fn mul_assign(&mut self, rhs: Number) {
119 use self::NumberOrPercentageUnit::*;
120 match *self {
121 IsNumber(ref mut length) => *length = *length * rhs,
122 IsPercentage(ref mut length) => *length = *length * rhs,
123 }
124 }
125}
126
127impl<Number: CssNumber> Div<Number> for NumberOrPercentageUnit<Number> {
128 type Output = Self;
129
130 #[inline(always)]
131 fn div(self, rhs: Number) -> Self::Output {
132 use self::NumberOrPercentageUnit::*;
133 match self {
134 IsNumber(length) => IsNumber(length / rhs),
135 IsPercentage(length) => IsPercentage(length / rhs),
136 }
137 }
138}
139
140impl<Number: CssNumber> DivAssign<Number> for NumberOrPercentageUnit<Number> {
141 #[inline(always)]
142 fn div_assign(&mut self, rhs: Number) {
143 use self::NumberOrPercentageUnit::*;
144 match *self {
145 IsNumber(ref mut length) => *length = *length / rhs,
146 IsPercentage(ref mut length) => *length = *length / rhs,
147 }
148 }
149}
150
151impl<Number: CssNumber> Rem<Number> for NumberOrPercentageUnit<Number> {
152 type Output = Self;
153
154 #[inline(always)]
155 fn rem(self, rhs: Number) -> Self::Output {
156 use self::NumberOrPercentageUnit::*;
157 match self {
158 IsNumber(length) => IsNumber(length % rhs),
159 IsPercentage(length) => IsPercentage(length % rhs),
160 }
161 }
162}
163
164impl<Number: CssNumber> RemAssign<Number> for NumberOrPercentageUnit<Number> {
165 #[inline(always)]
166 fn rem_assign(&mut self, rhs: Number) {
167 use self::NumberOrPercentageUnit::*;
168 match *self {
169 IsNumber(ref mut length) => *length = *length % rhs,
170 IsPercentage(ref mut length) => *length = *length % rhs,
171 }
172 }
173}
174
175impl<Number: CssNumber> Neg for NumberOrPercentageUnit<Number> {
176 type Output = Self;
177
178 #[inline(always)]
179 fn neg(self) -> Self::Output {
180 use self::NumberOrPercentageUnit::*;
181 match self {
182 IsNumber(length) => IsNumber(-length),
183 IsPercentage(length) => IsPercentage(-length),
184 }
185 }
186}
187
188impl<Number: CssNumber> CssNumberNewType<Number>
189 for NumberOrPercentageUnit<Number>
190{
191 #[inline(always)]
192 fn to_f32(&self) -> f32 {
193 self.to_CssNumber().to_f32()
194 }
195
196 #[inline(always)]
197 fn as_CssNumber(&self) -> &Number {
198 use self::NumberOrPercentageUnit::*;
199 match *self {
200 IsNumber(ref length) => length.as_CssNumber(),
201 IsPercentage(ref length) => length.as_CssNumber(),
202 }
203 }
204}
205
206impl<NumberX: CssNumber> Unit for NumberOrPercentageUnit<NumberX> {
207 type Number = NumberX;
208
209 const HasDimension: bool = true;
210
211 #[inline(always)]
212 fn parse_one_outside_calc_function<'i, 't>(
213 context: &ParserContext,
214 input: &mut Parser<'i, 't>,
215 ) -> Result<
216 CalculablePropertyValue<Self>,
217 ParseError<'i, CustomParseError<'i>>,
218 > {
219 let functionParser = match *input.next()? {
220 Token::Number {
221 value, int_value, ..
222 } => {
223 return Self::Number::parseNumber(value, int_value)
224 .map(|value| Constant(IsNumber(value)))
225 }
226
227 Token::Percentage { unit_value, .. } => {
228 return PercentageUnit::parse_percentage(unit_value)
229 .map(|value| Constant(IsPercentage(value)))
230 }
231
232 Token::Function(ref name) => FunctionParser::parser(name)?,
233
234 ref unexpectedToken => {
235 return CustomParseError::unexpectedToken(unexpectedToken)
236 }
237 };
238 functionParser.parse_one_outside_calc_function(context, input)
239 }
240
241 #[inline(always)]
242 fn parse_one_inside_calc_function<'i, 't>(
243 context: &ParserContext,
244 input: &mut Parser<'i, 't>,
245 ) -> Result<
246 Either<CalculablePropertyValue<Self>, CalcExpression<Self>>,
247 ParseError<'i, CustomParseError<'i>>,
248 > {
249 let functionParser = match *input.next()? {
250 Token::Number {
251 value, int_value, ..
252 } => {
253 return Self::Number::parseNumber(value, int_value)
254 .map(|value| Left(Constant(IsNumber(value))))
255 }
256
257 Token::Percentage { unit_value, .. } => {
258 return PercentageUnit::parse_percentage(unit_value)
259 .map(|value| Left(Constant(IsPercentage(value))))
260 }
261
262 Token::ParenthesisBlock => FunctionParser::parentheses,
263
264 Token::Function(ref name) => FunctionParser::parser(name)?,
265
266 ref unexpectedToken => {
267 return CustomParseError::unexpectedToken(unexpectedToken)
268 }
269 };
270 functionParser.parse_one_inside_calc_function(context, input)
271 }
272
273 #[inline(always)]
274 fn to_canonical_dimension_value<
275 Conversion: FontRelativeLengthConversion<Self::Number>
276 + ViewportPercentageLengthConversion<Self::Number>
277 + PercentageConversion<Self::Number>,
278 >(
279 &self,
280 conversion: &Conversion,
281 ) -> Self::Number {
282 use self::NumberOrPercentageUnit::*;
283
284 match *self {
285 IsNumber(ref length) => {
286 length.to_canonical_dimension_value(conversion)
287 }
288 IsPercentage(ref percentage) => {
289 percentage.to_canonical_dimension_value(conversion)
290 }
291 }
292 }
293
294 #[inline(always)]
295 fn from_raw_css_for_var_expression_evaluation(
296 value: &str,
297 _is_not_in_page_rule: bool,
298 ) -> Option<Self> {
299 use self::NumberOrPercentageUnit::*;
300
301 fn from_raw_css_for_var_expression_evaluation_internal<
302 'i: 't,
303 't,
304 Number: CssNumber,
305 >(
306 input: &mut Parser<'i, 't>,
307 ) -> Result<
308 NumberOrPercentageUnit<Number>,
309 ParseError<'i, CustomParseError<'i>>,
310 > {
311 let value = match *input.next()? {
312 Token::Number {
313 value, int_value, ..
314 } => Number::parseNumber(value, int_value).map(IsNumber),
315
316 Token::Percentage { unit_value, .. } => {
317 PercentageUnit::parse_percentage(unit_value)
318 .map(|value| IsPercentage(value))
319 }
320
321 ref unexpectedToken => {
322 CustomParseError::unexpectedToken(unexpectedToken)
323 }
324 };
325
326 input.skip_whitespace();
327
328 input.expect_exhausted()?;
329
330 value
331 }
332
333 const LineNumberingIsZeroBased: u32 = 0;
334
335 let mut parserInput = ParserInput::new_with_line_number_offset(
336 value,
337 LineNumberingIsZeroBased,
338 );
339 let mut input = Parser::new(&mut parserInput);
340
341 from_raw_css_for_var_expression_evaluation_internal(&mut input).ok()
342 }
343}
344
345impl<Number: CssNumber> NumberOrPercentageUnit<Number> {
346 #[inline(always)]
348 pub fn to_number<Conversion: PercentageConversion<Number>>(
349 &self,
350 conversion: &Conversion,
351 ) -> Number {
352 use self::NumberOrPercentageUnit::*;
353
354 match *self {
355 IsNumber(number) => number,
356 IsPercentage(percentage) => {
357 percentage.to_absolute_value(conversion)
358 }
359 }
360 }
361}