1use {
5 super::{
6 conversions::{
7 FontRelativeLengthConversion,
8 PercentageConversion,
9 ViewportPercentageLengthConversion,
10 },
11 AbsoluteLength::{self, *},
12 FontRelativeLength::{self, *},
13 LengthUnit::*,
14 PercentageUnit,
15 Unit,
16 ViewportPercentageLength::{self, *},
17 },
18 crate::{
19 domain::{
20 expressions::{
21 CalcExpression,
22 CalculablePropertyValue::{self, *},
23 FunctionParser,
24 },
25 numbers::{CssNumber, CssNumberNewType},
26 },
27 parsers::ParserContext,
28 CustomParseError::{self, *},
29 },
30 cssparser::{CowRcStr, ParseError, Parser, ParserInput, ToCss, Token},
31 either::{Either, Left},
32 std::{fmt, ops::*},
33};
34
35#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
39pub enum LengthUnit<Number: CssNumber> {
40 Absolute(AbsoluteLength<Number>),
44
45 FontRelative(FontRelativeLength<Number>),
49
50 ViewportPercentage(ViewportPercentageLength<Number>),
55}
56
57impl<Number: CssNumber> ToCss for LengthUnit<Number> {
58 fn to_css<W: fmt::Write>(&self, dest: &mut W) -> fmt::Result {
59 match *self {
60 Absolute(ref length) => length.to_css(dest),
61 FontRelative(ref length) => length.to_css(dest),
62 ViewportPercentage(ref length) => length.to_css(dest),
63 }
64 }
65}
66
67impl<Number: CssNumber> Default for LengthUnit<Number> {
68 #[inline(always)]
69 fn default() -> Self {
70 Absolute(AbsoluteLength::default())
71 }
72}
73
74impl<Number: CssNumber> Add<Number> for LengthUnit<Number> {
75 type Output = Self;
76
77 #[inline(always)]
78 fn add(self, rhs: Number) -> Self::Output {
79 match self {
80 Absolute(length) => Absolute(length + rhs),
81 FontRelative(length) => FontRelative(length + rhs),
82 ViewportPercentage(length) => ViewportPercentage(length + rhs),
83 }
84 }
85}
86
87impl<Number: CssNumber> AddAssign<Number> for LengthUnit<Number> {
88 #[inline(always)]
89 fn add_assign(&mut self, rhs: Number) {
90 match *self {
91 Absolute(ref mut length) => *length = *length + rhs,
92 FontRelative(ref mut length) => *length = *length + rhs,
93 ViewportPercentage(ref mut length) => *length = *length + rhs,
94 }
95 }
96}
97
98impl<Number: CssNumber> Sub<Number> for LengthUnit<Number> {
99 type Output = Self;
100
101 #[inline(always)]
102 fn sub(self, rhs: Number) -> Self::Output {
103 match self {
104 Absolute(length) => Absolute(length - rhs),
105 FontRelative(length) => FontRelative(length - rhs),
106 ViewportPercentage(length) => ViewportPercentage(length - rhs),
107 }
108 }
109}
110
111impl<Number: CssNumber> SubAssign<Number> for LengthUnit<Number> {
112 #[inline(always)]
113 fn sub_assign(&mut self, rhs: Number) {
114 match *self {
115 Absolute(ref mut length) => *length = *length - rhs,
116 FontRelative(ref mut length) => *length = *length - rhs,
117 ViewportPercentage(ref mut length) => *length = *length - rhs,
118 }
119 }
120}
121
122impl<Number: CssNumber> Mul<Number> for LengthUnit<Number> {
123 type Output = Self;
124
125 #[inline(always)]
126 fn mul(self, rhs: Number) -> Self::Output {
127 match self {
128 Absolute(length) => Absolute(length * rhs),
129 FontRelative(length) => FontRelative(length * rhs),
130 ViewportPercentage(length) => ViewportPercentage(length * rhs),
131 }
132 }
133}
134
135impl<Number: CssNumber> MulAssign<Number> for LengthUnit<Number> {
136 #[inline(always)]
137 fn mul_assign(&mut self, rhs: Number) {
138 match *self {
139 Absolute(ref mut length) => *length = *length * rhs,
140 FontRelative(ref mut length) => *length = *length * rhs,
141 ViewportPercentage(ref mut length) => *length = *length * rhs,
142 }
143 }
144}
145
146impl<Number: CssNumber> Div<Number> for LengthUnit<Number> {
147 type Output = Self;
148
149 #[inline(always)]
150 fn div(self, rhs: Number) -> Self::Output {
151 match self {
152 Absolute(length) => Absolute(length / rhs),
153 FontRelative(length) => FontRelative(length / rhs),
154 ViewportPercentage(length) => ViewportPercentage(length / rhs),
155 }
156 }
157}
158
159impl<Number: CssNumber> DivAssign<Number> for LengthUnit<Number> {
160 #[inline(always)]
161 fn div_assign(&mut self, rhs: Number) {
162 match *self {
163 Absolute(ref mut length) => *length = *length / rhs,
164 FontRelative(ref mut length) => *length = *length / rhs,
165 ViewportPercentage(ref mut length) => *length = *length / rhs,
166 }
167 }
168}
169
170impl<Number: CssNumber> Rem<Number> for LengthUnit<Number> {
171 type Output = LengthUnit<Number>;
172
173 #[inline(always)]
174 fn rem(self, rhs: Number) -> Self::Output {
175 match self {
176 Absolute(length) => Absolute(length % rhs),
177 FontRelative(length) => FontRelative(length % rhs),
178 ViewportPercentage(length) => ViewportPercentage(length % rhs),
179 }
180 }
181}
182
183impl<Number: CssNumber> RemAssign<Number> for LengthUnit<Number> {
184 #[inline(always)]
185 fn rem_assign(&mut self, rhs: Number) {
186 match *self {
187 Absolute(ref mut length) => *length = *length % rhs,
188 FontRelative(ref mut length) => *length = *length % rhs,
189 ViewportPercentage(ref mut length) => *length = *length % rhs,
190 }
191 }
192}
193
194impl<Number: CssNumber> Neg for LengthUnit<Number> {
195 type Output = LengthUnit<Number>;
196
197 #[inline(always)]
198 fn neg(self) -> Self::Output {
199 match self {
200 Absolute(length) => Absolute(-length),
201 FontRelative(length) => FontRelative(-length),
202 ViewportPercentage(length) => ViewportPercentage(-length),
203 }
204 }
205}
206
207impl<Number: CssNumber> CssNumberNewType<Number> for LengthUnit<Number> {
208 #[inline(always)]
209 fn to_f32(&self) -> f32 {
210 self.to_CssNumber().to_f32()
211 }
212
213 #[inline(always)]
214 fn as_CssNumber(&self) -> &Number {
215 match *self {
216 Absolute(ref length) => length.as_CssNumber(),
217 FontRelative(ref length) => length.as_CssNumber(),
218 ViewportPercentage(ref length) => length.as_CssNumber(),
219 }
220 }
221}
222
223impl<NumberX: CssNumber> Unit for LengthUnit<NumberX> {
224 type Number = NumberX;
225
226 const HasDimension: bool = true;
227
228 #[inline(always)]
229 fn parse_one_outside_calc_function<'i, 't>(
230 context: &ParserContext,
231 input: &mut Parser<'i, 't>,
232 ) -> Result<
233 CalculablePropertyValue<Self>,
234 ParseError<'i, CustomParseError<'i>>,
235 > {
236 let functionParser = match *input.next()? {
237 Token::Number { value, .. } => {
238 return Self::parseUnitLessNumber(
239 value,
240 context.parsing_mode_allows_unitless_lengths(),
241 )
242 .map(Constant)
243 }
244
245 Token::Dimension {
246 value, ref unit, ..
247 } => {
248 return Self::parseDimension(
249 value,
250 unit,
251 context.isNotInPageRule(),
252 )
253 .map(Constant)
254 }
255
256 Token::Function(ref name) => FunctionParser::parser(name)?,
257
258 ref unexpectedToken => {
259 return CustomParseError::unexpectedToken(unexpectedToken)
260 }
261 };
262 functionParser.parse_one_outside_calc_function(context, input)
263 }
264
265 #[inline(always)]
266 fn parse_one_inside_calc_function<'i, 't>(
267 context: &ParserContext,
268 input: &mut Parser<'i, 't>,
269 ) -> Result<
270 Either<CalculablePropertyValue<Self>, CalcExpression<Self>>,
271 ParseError<'i, CustomParseError<'i>>,
272 > {
273 let functionParser = match *input.next()? {
274 Token::Number { value, .. } => {
275 return Self::number_inside_calc_function(value)
276 }
277
278 Token::Percentage { unit_value, .. } => {
279 return PercentageUnit::parse_percentage(unit_value)
280 .map(|value| Left(Percentage(value)))
281 }
282
283 Token::Dimension {
284 value, ref unit, ..
285 } => {
286 return Self::parseDimension(
287 value,
288 unit,
289 context.isNotInPageRule(),
290 )
291 .map(|value| Left(Constant(value)))
292 }
293
294 Token::ParenthesisBlock => FunctionParser::parentheses,
295
296 Token::Function(ref name) => FunctionParser::parser(name)?,
297
298 ref unexpectedToken => {
299 return CustomParseError::unexpectedToken(unexpectedToken)
300 }
301 };
302 functionParser.parse_one_inside_calc_function(context, input)
303 }
304
305 #[inline(always)]
306 fn to_canonical_dimension(self) -> Self {
307 match self {
308 Absolute(ref length) => Absolute(px(length.to_px())),
309 unchanged => unchanged,
310 }
311 }
312
313 #[inline(always)]
314 fn to_canonical_dimension_value<
315 Conversion: FontRelativeLengthConversion<Self::Number>
316 + ViewportPercentageLengthConversion<Self::Number>
317 + PercentageConversion<Self::Number>,
318 >(
319 &self,
320 conversion: &Conversion,
321 ) -> Self::Number {
322 self.to_px(conversion)
323 }
324
325 #[inline(always)]
326 fn from_raw_css_for_var_expression_evaluation(
327 value: &str,
328 is_not_in_page_rule: bool,
329 ) -> Option<Self> {
330 fn from_raw_css_for_var_expression_evaluation_internal<
331 'i: 't,
332 't,
333 Number: CssNumber,
334 >(
335 is_not_in_page_rule: bool,
336 input: &mut Parser<'i, 't>,
337 ) -> Result<LengthUnit<Number>, ParseError<'i, CustomParseError<'i>>>
338 {
339 let value = match *input.next()? {
340 Token::Number { value, .. } => {
341 LengthUnit::parseUnitLessNumber(value, false)
342 }
343
344 Token::Dimension {
345 value, ref unit, ..
346 } => {
347 LengthUnit::parseDimension(value, unit, is_not_in_page_rule)
348 }
349
350 ref unexpectedToken => {
351 CustomParseError::unexpectedToken(unexpectedToken)
352 }
353 };
354
355 input.skip_whitespace();
356
357 input.expect_exhausted()?;
358
359 value
360 }
361
362 const LineNumberingIsZeroBased: u32 = 0;
363
364 let mut parserInput = ParserInput::new_with_line_number_offset(
365 value,
366 LineNumberingIsZeroBased,
367 );
368 let mut input = Parser::new(&mut parserInput);
369
370 from_raw_css_for_var_expression_evaluation_internal(
371 is_not_in_page_rule,
372 &mut input,
373 )
374 .ok()
375 }
376}
377
378impl<Number: CssNumber> LengthUnit<Number> {
379 #[inline]
381 pub fn is_absolute_zero(&self) -> bool {
382 match *self {
383 Absolute(length) => length.is_zero(),
384 _ => false,
385 }
386 }
387
388 #[inline(always)]
389 pub fn is_absolute_length(&self) -> bool {
390 match *self {
391 Absolute(..) => true,
392 FontRelative(..) | ViewportPercentage(..) => false,
393 }
394 }
395
396 #[inline(always)]
398 pub fn to_px<
399 Conversion: FontRelativeLengthConversion<Number>
400 + ViewportPercentageLengthConversion<Number>,
401 >(
402 &self,
403 conversion: &Conversion,
404 ) -> Number {
405 match *self {
406 Absolute(ref length) => length.to_px(),
407 FontRelative(ref length) => length.to_px(conversion),
408 ViewportPercentage(ref length) => length.to_px(conversion),
409 }
410 }
411
412 #[inline]
414 pub fn to_app_units<
415 Conversion: FontRelativeLengthConversion<Number>
416 + ViewportPercentageLengthConversion<Number>,
417 >(
418 &self,
419 conversion: &Conversion,
420 ) -> Number {
421 match *self {
422 Absolute(ref length) => length.to_app_units(),
423 FontRelative(ref length) => length.to_app_units(conversion),
424 ViewportPercentage(ref length) => length.to_app_units(conversion),
425 }
426 }
427
428 #[inline(always)]
429 pub(crate) fn parseUnitLessNumber<'i>(
430 value: f32,
431 parsing_mode_allows_unitless_lengths: bool,
432 ) -> Result<Self, ParseError<'i, CustomParseError<'i>>> {
433 if value == 0. {
434 Ok(Self::default())
435 } else if parsing_mode_allows_unitless_lengths {
436 let cssNumber =
437 Number::new(value).map_err(|cssNumberConversionError| {
438 ParseError::from(CouldNotParseCssSignedNumber(
439 cssNumberConversionError,
440 value,
441 ))
442 })?;
443 Ok(Absolute(px(cssNumber)))
444 } else {
445 CustomParseError::dimensionless(value)
446 }
447 }
448
449 #[inline(always)]
450 pub(crate) fn parseDimension<'i>(
451 value: f32,
452 unit: &CowRcStr<'i>,
453 is_not_in_page_rule: bool,
454 ) -> Result<Self, ParseError<'i, CustomParseError<'i>>> {
455 let cssNumber =
456 Number::new(value).map_err(|cssNumberConversionError| {
457 ParseError::from(CouldNotParseCssSignedNumber(
458 cssNumberConversionError,
459 value,
460 ))
461 })?;
462
463 match_ignore_ascii_case! {
464 &*unit,
465
466 "px" => Ok(Absolute(px(cssNumber))),
467
468 "in" => Ok(Absolute(in_(cssNumber))),
469
470 "cm" => Ok(Absolute(cm(cssNumber))),
471
472 "mm" => Ok(Absolute(mm(cssNumber))),
473
474 "q" => Ok(Absolute(q(cssNumber))),
475
476 "pt" => Ok(Absolute(pt(cssNumber))),
477
478 "pc" => Ok(Absolute(pc(cssNumber))),
479
480 "em" => if is_not_in_page_rule
481 {
482 Ok(FontRelative(em(cssNumber)))
483 }
484 else
485 {
486 Err(ParseError::from(CustomParseError::FontRelativeLengthsAreNotAllowedInAPageAtRule))
487 },
488
489 "ex" => if is_not_in_page_rule
490 {
491 Ok(FontRelative(ex(cssNumber)))
492 }
493 else
494 {
495 Err(ParseError::from(CustomParseError::FontRelativeLengthsAreNotAllowedInAPageAtRule))
496 },
497
498 "ch" => Ok(FontRelative(ch(cssNumber))),
499
500 "rem" => Ok(FontRelative(rem(cssNumber))),
501
502 "vw" => if is_not_in_page_rule
503 {
504 Ok(ViewportPercentage(vw(cssNumber)))
505 }
506 else
507 {
508 Err(ParseError::from(CustomParseError::ViewportLengthsAreNotAllowedInAPageAtRule))
509 },
510
511 "vh" => if is_not_in_page_rule
512 {
513 Ok(ViewportPercentage(vh(cssNumber)))
514 }
515 else
516 {
517 Err(ParseError::from(CustomParseError::ViewportLengthsAreNotAllowedInAPageAtRule))
518 },
519
520 "vmin" => if is_not_in_page_rule
521 {
522 Ok(ViewportPercentage(vmin(cssNumber)))
523 }
524 else
525 {
526 Err(ParseError::from(CustomParseError::ViewportLengthsAreNotAllowedInAPageAtRule))
527 },
528
529 "vmax" => if is_not_in_page_rule
530 {
531 Ok(ViewportPercentage(vmax(cssNumber)))
532 }
533 else
534 {
535 Err(ParseError::from(CustomParseError::ViewportLengthsAreNotAllowedInAPageAtRule))
536 },
537
538 _ => Err(ParseError::from(CouldNotParseDimension(value, unit.clone()))),
539 }
540 }
541}