lewp_css/domain/expressions/
var_expression.rs

1// This file is part of css. It is subject to the license terms in the COPYRIGHT file found in the top-level directory of this distribution and at https://raw.githubusercontent.com/lemonrock/css/master/COPYRIGHT. No part of predicator, including this file, may be copied, modified, propagated, or distributed except according to the terms contained in the COPYRIGHT file.
2// Copyright © 2017 The developers of css. See the COPYRIGHT file in the top-level directory of this distribution and at https://raw.githubusercontent.com/lemonrock/css/master/COPYRIGHT.
3
4use {
5    super::Expression,
6    crate::{
7        domain::units::{conversions::*, Unit},
8        parsers::ParserContext,
9        CustomParseError,
10    },
11    cssparser::{serialize_identifier, ParseError, Parser, ToCss},
12    std::fmt,
13};
14
15#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
16pub struct VarExpression {
17    pub custom_property_lower_case_name_without_double_dash: String,
18
19    pub default_value_css: Option<String>,
20
21    pub is_not_in_page_rule: bool,
22}
23
24impl ToCss for VarExpression {
25    fn to_css<W: fmt::Write>(&self, dest: &mut W) -> fmt::Result {
26        dest.write_str("--")?;
27        serialize_identifier(
28            &self.custom_property_lower_case_name_without_double_dash,
29            dest,
30        )?;
31
32        if let Some(ref default_value) = self.default_value_css {
33            dest.write_char(',')?;
34            dest.write_str(default_value)?;
35        }
36
37        Ok(())
38    }
39}
40
41impl<U: Unit> Expression<U> for VarExpression {
42    /// Division by zero is handled by returning the maximum possible f32 value
43    /// Subtractions for UnsignedCssNumber that are negative are handled by returning 0.0
44    #[inline(always)]
45    fn evaluate<
46        Conversion: FontRelativeLengthConversion<U::Number>
47            + ViewportPercentageLengthConversion<U::Number>
48            + PercentageConversion<U::Number>
49            + AttributeConversion<U>
50            + CssVariableConversion,
51    >(
52        &self,
53        conversion: &Conversion,
54    ) -> Option<U::Number> {
55        match conversion.cssVariableValue(
56            &self.custom_property_lower_case_name_without_double_dash,
57        ) {
58            Some(value_css) => U::from_raw_css_for_var_expression_evaluation(
59                value_css,
60                self.is_not_in_page_rule,
61            )
62            .map(|unit| unit.to_CssNumber()),
63            None => {
64                if let Some(ref value_css) = self.default_value_css {
65                    U::from_raw_css_for_var_expression_evaluation(
66                        value_css,
67                        self.is_not_in_page_rule,
68                    )
69                    .map(|unit| unit.to_CssNumber())
70                } else {
71                    None
72                }
73            }
74        }
75    }
76}
77
78impl VarExpression {
79    #[inline(always)]
80    pub(crate) fn parse<'i, 't>(
81        context: &ParserContext,
82        input: &mut Parser<'i, 't>,
83    ) -> Result<Self, ParseError<'i, CustomParseError<'i>>> {
84        input.parse_nested_block(|input| {
85            let custom_property_lower_case_name_without_double_dash = {
86                let identifier = input.expect_ident()?;
87                if !identifier.starts_with("--") {
88                    return Err(ParseError::from(
89                        CustomParseError::CssVariablesInVarExpressionsMustStartWithTwoDashes(
90                            identifier.clone(),
91                        ),
92                    ));
93                }
94                let mut custom_property_lower_case_name_without_double_dash =
95                    (&identifier[2..]).to_owned();
96                custom_property_lower_case_name_without_double_dash.make_ascii_lowercase();
97                custom_property_lower_case_name_without_double_dash
98            };
99
100            let startPosition = input.position();
101
102            let result = input.r#try(|input| {
103                input.expect_comma()?;
104                input.skip_whitespace();
105
106                if input.is_exhausted() {
107                    Ok(None)
108                } else {
109                    let result: Result<_, ParseError<CustomParseError>> =
110                        input.parse_entirely(|input| {
111                            Ok(Some(input.slice_from(startPosition).to_owned()))
112                        });
113                    result
114                }
115            });
116
117            let default_value_css = if let Ok(Some(default_value_css)) = result {
118                if default_value_css.is_empty() {
119                    None
120                } else {
121                    Some(default_value_css)
122                }
123            } else {
124                None
125            };
126
127            Ok(Self {
128                custom_property_lower_case_name_without_double_dash,
129                default_value_css,
130                is_not_in_page_rule: context.isNotInPageRule(),
131            })
132        })
133    }
134}