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