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 ResolutionUnit::*,
37};
38
39#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
41pub enum ResolutionUnit<Number: CssNumber> {
42 dpi(Number),
44
45 dppx(Number),
47
48 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}