Skip to main content

style/typed_om/
numeric_declaration.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 Numeric Declaration.
6
7use crate::derives::*;
8use crate::parser::{Parse, ParserContext};
9use crate::typed_om::numeric::NoCalcNumeric;
10use crate::values::generics::calc::CalcUnits;
11use crate::values::specified::calc::{CalcNode, CalcParseFlags};
12use crate::values::specified::{
13    NoCalcAngle, NoCalcLength, NoCalcNumber, NoCalcPercentage, NoCalcTime,
14};
15use cssparser::{Parser, Token};
16use style_traits::values::specified::AllowedNumericType;
17use style_traits::{ParseError, StyleParseErrorKind};
18
19/// A numeric declaration, with or without a `calc()` expression.
20#[derive(Clone, ToTyped)]
21pub enum NumericDeclaration {
22    /// A numeric value without a `calc()` expression.
23    NoCalc(NoCalcNumeric),
24
25    /// A numeric value represented by a `calc()` expression.
26    ///
27    /// <https://drafts.csswg.org/css-values/#calc-notation>
28    Calc(CalcNode),
29}
30
31impl Parse for NumericDeclaration {
32    /// <https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-parse>
33    fn parse<'i, 't>(
34        context: &ParserContext,
35        input: &mut Parser<'i, 't>,
36    ) -> Result<Self, ParseError<'i>> {
37        let location = input.current_source_location();
38
39        // Step 1.
40        let token = input.next()?;
41
42        // Step 2.
43        match *token {
44            Token::Number { value, .. } => Ok(Self::NoCalc(NoCalcNumeric::Number(
45                NoCalcNumber::new(value),
46            ))),
47
48            Token::Percentage { unit_value, .. } => Ok(Self::NoCalc(NoCalcNumeric::Percentage(
49                NoCalcPercentage::new(unit_value),
50            ))),
51
52            Token::Dimension {
53                value, ref unit, ..
54            } => {
55                if let Ok(length) = NoCalcLength::parse_dimension_with_context(context, value, unit)
56                {
57                    return Ok(Self::NoCalc(NoCalcNumeric::Length(length)));
58                }
59
60                if let Ok(angle) = NoCalcAngle::parse_dimension(value, unit) {
61                    return Ok(Self::NoCalc(NoCalcNumeric::Angle(angle)));
62                }
63
64                if let Ok(time) = NoCalcTime::parse_dimension(value, unit) {
65                    return Ok(Self::NoCalc(NoCalcNumeric::Time(time)));
66                }
67
68                Err(location.new_unexpected_token_error(token.clone()))
69
70                // Step 3.
71
72                // TODO: A type should be created from unit and if that fails, the failure
73                // should be propagated here.
74            },
75
76            Token::Function(ref name) => {
77                let function = CalcNode::math_function(context, name, location)?;
78                let allow_all_units = CalcParseFlags::new(CalcUnits::ALL);
79                let node = CalcNode::parse(context, input, function, allow_all_units)?;
80
81                let allow_all_types = AllowedNumericType::All;
82                let _ = node
83                    .clone()
84                    .into_length_or_percentage(allow_all_types)
85                    .map_err(|()| {
86                        location.new_custom_error(StyleParseErrorKind::UnspecifiedError)
87                    })?;
88
89                // TODO: Add support for other values represented by a `calc()` expression.
90
91                Ok(Self::Calc(node))
92            },
93
94            ref token => return Err(location.new_unexpected_token_error(token.clone())),
95        }
96    }
97}