lewp_css/domain/units/
resolution_unit.rs

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