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