lewp_css/domain/expressions/
attr_expression.rs1use {
5 super::{Expression, TypeOrUnit},
6 crate::{
7 domain::units::{
8 conversions::{
9 AttributeConversion,
10 CssVariableConversion,
11 FontRelativeLengthConversion,
12 PercentageConversion,
13 ViewportPercentageLengthConversion,
14 },
15 Unit,
16 },
17 parsers::ParserContext,
18 CustomParseError,
19 },
20 cssparser::{serialize_identifier, ParseError, Parser, ToCss},
21 std::fmt,
22};
23
24#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
25pub struct AttrExpression {
26 pub attribute_lower_case_name: String,
27
28 pub type_or_unit: TypeOrUnit,
29
30 pub default_value_css: Option<String>,
31
32 pub is_not_in_page_rule: bool,
33}
34
35impl ToCss for AttrExpression {
36 fn to_css<W: fmt::Write>(&self, dest: &mut W) -> fmt::Result {
37 serialize_identifier(&self.attribute_lower_case_name, dest)?;
38
39 if self.type_or_unit != TypeOrUnit::string {
40 dest.write_char(' ')?;
41 self.type_or_unit.to_css(dest)?;
42 }
43
44 if let Some(ref default_value_css) = self.default_value_css {
45 dest.write_char(',')?;
46 dest.write_str(default_value_css)?;
47 }
48
49 Ok(())
50 }
51}
52
53impl<U: Unit> Expression<U> for AttrExpression {
54 #[inline(always)]
57 fn evaluate<
58 Conversion: FontRelativeLengthConversion<U::Number>
59 + ViewportPercentageLengthConversion<U::Number>
60 + PercentageConversion<U::Number>
61 + AttributeConversion<U>
62 + CssVariableConversion,
63 >(
64 &self,
65 conversion: &Conversion,
66 ) -> Option<U::Number> {
67 self.to_unit(conversion).map(|unit| unit.to_CssNumber())
68 }
69}
70
71impl AttrExpression {
72 pub fn to_unit<U: Unit, Conversion: AttributeConversion<U>>(
73 &self,
74 conversion: &Conversion,
75 ) -> Option<U> {
76 let (possibleValue, propertyDefaultOrIfNoPropertyDefaultTheUnitDefault) =
77 conversion.attributeValue(&self.attribute_lower_case_name);
78 if let Some(value) = possibleValue {
79 if let Ok(ref value_css) = self.type_or_unit.value_to_css(value)
80 {
81 U::from_raw_css_for_var_expression_evaluation(
82 value_css,
83 self.is_not_in_page_rule,
84 )
85 } else if let Some(ref value_css) = self.default_value_css {
86 U::from_raw_css_for_var_expression_evaluation(
87 value_css,
88 self.is_not_in_page_rule,
89 )
90 } else {
91 Some(propertyDefaultOrIfNoPropertyDefaultTheUnitDefault)
92 }
93 } else if let Some(ref value_css) = self.default_value_css {
94 U::from_raw_css_for_var_expression_evaluation(
95 value_css,
96 self.is_not_in_page_rule,
97 )
98 } else {
99 Some(propertyDefaultOrIfNoPropertyDefaultTheUnitDefault)
100 }
101 }
102
103 #[inline(always)]
104 pub(crate) fn parse<'i, 't>(
105 context: &ParserContext,
106 input: &mut Parser<'i, 't>,
107 ) -> Result<Self, ParseError<'i, CustomParseError<'i>>> {
108 input.parse_nested_block(|input| {
109 let attribute_lower_case_name = {
110 let attribute = input.expect_ident()?;
111 attribute.to_ascii_lowercase()
112 };
113
114 input.skip_whitespace();
115
116 if input.is_exhausted() {
117 Ok(Self {
118 attribute_lower_case_name,
119 type_or_unit: TypeOrUnit::default(),
120 default_value_css: None,
121 is_not_in_page_rule: context.isNotInPageRule(),
122 })
123 } else {
124 let type_or_unit = if let Ok(type_or_unit) = input.r#try(TypeOrUnit::parse)
125 {
126 type_or_unit
127 } else {
128 TypeOrUnit::default()
129 };
130
131 let result = input.r#try(|input| {
132 input.skip_whitespace();
133 input.expect_comma()?;
134 input.skip_whitespace();
135
136 let startPosition = input.position();
137 let result: Result<_, ParseError<CustomParseError>> = input
138 .parse_entirely(|input| {
139 Ok(input.slice_from(startPosition).to_owned())
140 });
141 result
142 });
143
144 let default_value_css = if let Ok(default_value_css) = result {
145 if default_value_css.is_empty() {
146 None
147 } else {
148 Some(default_value_css)
149 }
150 } else {
151 None
152 };
153
154 Ok(Self {
155 attribute_lower_case_name,
156 type_or_unit,
157 default_value_css,
158 is_not_in_page_rule: context.isNotInPageRule(),
159 })
160 }
161 })
162 }
163}