lewp_css/domain/units/
number_or_percentage_unit.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::{
6        conversions::{
7            FontRelativeLengthConversion,
8            PercentageConversion,
9            ViewportPercentageLengthConversion,
10        },
11        PercentageUnit,
12        Unit,
13    },
14    crate::{
15        domain::{
16            expressions::{
17                CalcExpression,
18                CalculablePropertyValue::{self, *},
19                FunctionParser,
20            },
21            numbers::{CssNumber, CssNumberNewType},
22        },
23        parsers::ParserContext,
24        CustomParseError,
25    },
26    cssparser::{ParseError, Parser, ParserInput, ToCss, Token},
27    either::{Either, Left},
28    std::{fmt, ops::*},
29    NumberOrPercentageUnit::*,
30};
31
32#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
33pub enum NumberOrPercentageUnit<Number: CssNumber> {
34    IsNumber(Number),
35    IsPercentage(PercentageUnit<Number>),
36}
37
38impl<Number: CssNumber> ToCss for NumberOrPercentageUnit<Number> {
39    fn to_css<W: fmt::Write>(&self, dest: &mut W) -> fmt::Result {
40        use self::NumberOrPercentageUnit::*;
41        match *self {
42            IsNumber(ref length) => length.to_css(dest),
43            IsPercentage(ref length) => length.to_css(dest),
44        }
45    }
46}
47
48impl<Number: CssNumber> Default for NumberOrPercentageUnit<Number> {
49    #[inline(always)]
50    fn default() -> Self {
51        NumberOrPercentageUnit::IsNumber(Number::default())
52    }
53}
54
55impl<Number: CssNumber> Add<Number> for NumberOrPercentageUnit<Number> {
56    type Output = Self;
57
58    #[inline(always)]
59    fn add(self, rhs: Number) -> Self::Output {
60        use self::NumberOrPercentageUnit::*;
61        match self {
62            IsNumber(length) => IsNumber(length + rhs),
63            IsPercentage(length) => IsPercentage(length + rhs),
64        }
65    }
66}
67
68impl<Number: CssNumber> AddAssign<Number> for NumberOrPercentageUnit<Number> {
69    #[inline(always)]
70    fn add_assign(&mut self, rhs: Number) {
71        use self::NumberOrPercentageUnit::*;
72        match *self {
73            IsNumber(ref mut length) => *length = *length + rhs,
74            IsPercentage(ref mut length) => *length = *length + rhs,
75        }
76    }
77}
78
79impl<Number: CssNumber> Sub<Number> for NumberOrPercentageUnit<Number> {
80    type Output = Self;
81
82    #[inline(always)]
83    fn sub(self, rhs: Number) -> Self::Output {
84        use self::NumberOrPercentageUnit::*;
85        match self {
86            IsNumber(length) => IsNumber(length - rhs),
87            IsPercentage(length) => IsPercentage(length - rhs),
88        }
89    }
90}
91
92impl<Number: CssNumber> SubAssign<Number> for NumberOrPercentageUnit<Number> {
93    #[inline(always)]
94    fn sub_assign(&mut self, rhs: Number) {
95        use self::NumberOrPercentageUnit::*;
96        match *self {
97            IsNumber(ref mut length) => *length = *length - rhs,
98            IsPercentage(ref mut length) => *length = *length - rhs,
99        }
100    }
101}
102
103impl<Number: CssNumber> Mul<Number> for NumberOrPercentageUnit<Number> {
104    type Output = Self;
105
106    #[inline(always)]
107    fn mul(self, rhs: Number) -> Self::Output {
108        use self::NumberOrPercentageUnit::*;
109        match self {
110            IsNumber(length) => IsNumber(length * rhs),
111            IsPercentage(length) => IsPercentage(length * rhs),
112        }
113    }
114}
115
116impl<Number: CssNumber> MulAssign<Number> for NumberOrPercentageUnit<Number> {
117    #[inline(always)]
118    fn mul_assign(&mut self, rhs: Number) {
119        use self::NumberOrPercentageUnit::*;
120        match *self {
121            IsNumber(ref mut length) => *length = *length * rhs,
122            IsPercentage(ref mut length) => *length = *length * rhs,
123        }
124    }
125}
126
127impl<Number: CssNumber> Div<Number> for NumberOrPercentageUnit<Number> {
128    type Output = Self;
129
130    #[inline(always)]
131    fn div(self, rhs: Number) -> Self::Output {
132        use self::NumberOrPercentageUnit::*;
133        match self {
134            IsNumber(length) => IsNumber(length / rhs),
135            IsPercentage(length) => IsPercentage(length / rhs),
136        }
137    }
138}
139
140impl<Number: CssNumber> DivAssign<Number> for NumberOrPercentageUnit<Number> {
141    #[inline(always)]
142    fn div_assign(&mut self, rhs: Number) {
143        use self::NumberOrPercentageUnit::*;
144        match *self {
145            IsNumber(ref mut length) => *length = *length / rhs,
146            IsPercentage(ref mut length) => *length = *length / rhs,
147        }
148    }
149}
150
151impl<Number: CssNumber> Rem<Number> for NumberOrPercentageUnit<Number> {
152    type Output = Self;
153
154    #[inline(always)]
155    fn rem(self, rhs: Number) -> Self::Output {
156        use self::NumberOrPercentageUnit::*;
157        match self {
158            IsNumber(length) => IsNumber(length % rhs),
159            IsPercentage(length) => IsPercentage(length % rhs),
160        }
161    }
162}
163
164impl<Number: CssNumber> RemAssign<Number> for NumberOrPercentageUnit<Number> {
165    #[inline(always)]
166    fn rem_assign(&mut self, rhs: Number) {
167        use self::NumberOrPercentageUnit::*;
168        match *self {
169            IsNumber(ref mut length) => *length = *length % rhs,
170            IsPercentage(ref mut length) => *length = *length % rhs,
171        }
172    }
173}
174
175impl<Number: CssNumber> Neg for NumberOrPercentageUnit<Number> {
176    type Output = Self;
177
178    #[inline(always)]
179    fn neg(self) -> Self::Output {
180        use self::NumberOrPercentageUnit::*;
181        match self {
182            IsNumber(length) => IsNumber(-length),
183            IsPercentage(length) => IsPercentage(-length),
184        }
185    }
186}
187
188impl<Number: CssNumber> CssNumberNewType<Number>
189    for NumberOrPercentageUnit<Number>
190{
191    #[inline(always)]
192    fn to_f32(&self) -> f32 {
193        self.to_CssNumber().to_f32()
194    }
195
196    #[inline(always)]
197    fn as_CssNumber(&self) -> &Number {
198        use self::NumberOrPercentageUnit::*;
199        match *self {
200            IsNumber(ref length) => length.as_CssNumber(),
201            IsPercentage(ref length) => length.as_CssNumber(),
202        }
203    }
204}
205
206impl<NumberX: CssNumber> Unit for NumberOrPercentageUnit<NumberX> {
207    type Number = NumberX;
208
209    const HasDimension: bool = true;
210
211    #[inline(always)]
212    fn parse_one_outside_calc_function<'i, 't>(
213        context: &ParserContext,
214        input: &mut Parser<'i, 't>,
215    ) -> Result<
216        CalculablePropertyValue<Self>,
217        ParseError<'i, CustomParseError<'i>>,
218    > {
219        let functionParser = match *input.next()? {
220            Token::Number {
221                value, int_value, ..
222            } => {
223                return Self::Number::parseNumber(value, int_value)
224                    .map(|value| Constant(IsNumber(value)))
225            }
226
227            Token::Percentage { unit_value, .. } => {
228                return PercentageUnit::parse_percentage(unit_value)
229                    .map(|value| Constant(IsPercentage(value)))
230            }
231
232            Token::Function(ref name) => FunctionParser::parser(name)?,
233
234            ref unexpectedToken => {
235                return CustomParseError::unexpectedToken(unexpectedToken)
236            }
237        };
238        functionParser.parse_one_outside_calc_function(context, input)
239    }
240
241    #[inline(always)]
242    fn parse_one_inside_calc_function<'i, 't>(
243        context: &ParserContext,
244        input: &mut Parser<'i, 't>,
245    ) -> Result<
246        Either<CalculablePropertyValue<Self>, CalcExpression<Self>>,
247        ParseError<'i, CustomParseError<'i>>,
248    > {
249        let functionParser = match *input.next()? {
250            Token::Number {
251                value, int_value, ..
252            } => {
253                return Self::Number::parseNumber(value, int_value)
254                    .map(|value| Left(Constant(IsNumber(value))))
255            }
256
257            Token::Percentage { unit_value, .. } => {
258                return PercentageUnit::parse_percentage(unit_value)
259                    .map(|value| Left(Constant(IsPercentage(value))))
260            }
261
262            Token::ParenthesisBlock => FunctionParser::parentheses,
263
264            Token::Function(ref name) => FunctionParser::parser(name)?,
265
266            ref unexpectedToken => {
267                return CustomParseError::unexpectedToken(unexpectedToken)
268            }
269        };
270        functionParser.parse_one_inside_calc_function(context, input)
271    }
272
273    #[inline(always)]
274    fn to_canonical_dimension_value<
275        Conversion: FontRelativeLengthConversion<Self::Number>
276            + ViewportPercentageLengthConversion<Self::Number>
277            + PercentageConversion<Self::Number>,
278    >(
279        &self,
280        conversion: &Conversion,
281    ) -> Self::Number {
282        use self::NumberOrPercentageUnit::*;
283
284        match *self {
285            IsNumber(ref length) => {
286                length.to_canonical_dimension_value(conversion)
287            }
288            IsPercentage(ref percentage) => {
289                percentage.to_canonical_dimension_value(conversion)
290            }
291        }
292    }
293
294    #[inline(always)]
295    fn from_raw_css_for_var_expression_evaluation(
296        value: &str,
297        _is_not_in_page_rule: bool,
298    ) -> Option<Self> {
299        use self::NumberOrPercentageUnit::*;
300
301        fn from_raw_css_for_var_expression_evaluation_internal<
302            'i: 't,
303            't,
304            Number: CssNumber,
305        >(
306            input: &mut Parser<'i, 't>,
307        ) -> Result<
308            NumberOrPercentageUnit<Number>,
309            ParseError<'i, CustomParseError<'i>>,
310        > {
311            let value = match *input.next()? {
312                Token::Number {
313                    value, int_value, ..
314                } => Number::parseNumber(value, int_value).map(IsNumber),
315
316                Token::Percentage { unit_value, .. } => {
317                    PercentageUnit::parse_percentage(unit_value)
318                        .map(|value| IsPercentage(value))
319                }
320
321                ref unexpectedToken => {
322                    CustomParseError::unexpectedToken(unexpectedToken)
323                }
324            };
325
326            input.skip_whitespace();
327
328            input.expect_exhausted()?;
329
330            value
331        }
332
333        const LineNumberingIsZeroBased: u32 = 0;
334
335        let mut parserInput = ParserInput::new_with_line_number_offset(
336            value,
337            LineNumberingIsZeroBased,
338        );
339        let mut input = Parser::new(&mut parserInput);
340
341        from_raw_css_for_var_expression_evaluation_internal(&mut input).ok()
342    }
343}
344
345impl<Number: CssNumber> NumberOrPercentageUnit<Number> {
346    /// Get an absolute number using a conversion
347    #[inline(always)]
348    pub fn to_number<Conversion: PercentageConversion<Number>>(
349        &self,
350        conversion: &Conversion,
351    ) -> Number {
352        use self::NumberOrPercentageUnit::*;
353
354        match *self {
355            IsNumber(number) => number,
356            IsPercentage(percentage) => {
357                percentage.to_absolute_value(conversion)
358            }
359        }
360    }
361}