1use {
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#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
41pub enum TimeUnit<Number: CssNumber> {
42 s(Number),
44
45 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}