lewp_css/domain/numbers/
css_signed_number.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::{CssNumber, CssNumberNewType},
6    crate::{
7        domain::{
8            expressions::{
9                CalcExpression,
10                CalculablePropertyValue::{self, *},
11                FunctionParser,
12            },
13            units::{
14                conversions::*,
15                AppUnitsPer,
16                PercentageUnit,
17                Unit,
18                UnitFromStrError,
19            },
20        },
21        parsers::ParserContext,
22        CustomParseError,
23    },
24    cssparser::{ParseError, Parser, ParserInput, ToCss, Token},
25    either::{Either, Left},
26    std::{
27        cmp::Ordering,
28        fmt::{self, Display, Formatter, LowerExp, UpperExp},
29        hash::{Hash, Hasher},
30        ops::*,
31        str::FromStr,
32    },
33};
34
35/// A CSS float value similar to f32 but with a more restricted range
36#[derive(Debug, Copy, Clone)]
37pub struct CssSignedNumber(f32);
38
39impl PartialEq for CssSignedNumber {
40    #[inline(always)]
41    fn eq(&self, other: &Self) -> bool {
42        self.to_f32().eq(&other.0)
43    }
44}
45
46impl Eq for CssSignedNumber {}
47
48impl PartialOrd for CssSignedNumber {
49    #[inline(always)]
50    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
51        self.to_f32().partial_cmp(&other.0)
52    }
53}
54
55impl Ord for CssSignedNumber {
56    #[inline(always)]
57    fn cmp(&self, other: &Self) -> Ordering {
58        self.partial_cmp(other).unwrap_or(Ordering::Equal)
59    }
60}
61
62impl Hash for CssSignedNumber {
63    #[inline(always)]
64    fn hash<H: Hasher>(&self, state: &mut H) {
65        self.to_bits().hash(state)
66    }
67}
68
69impl ToCss for CssSignedNumber {
70    #[inline(always)]
71    fn to_css<W: fmt::Write>(&self, dest: &mut W) -> fmt::Result {
72        self.to_f32().to_css(dest)
73    }
74}
75
76impl Display for CssSignedNumber {
77    #[inline(always)]
78    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
79        <f32 as Display>::fmt(&self.to_f32(), fmt)
80    }
81}
82
83impl LowerExp for CssSignedNumber {
84    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
85        <f32 as LowerExp>::fmt(&self.to_f32(), f)
86    }
87}
88
89impl UpperExp for CssSignedNumber {
90    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
91        <f32 as UpperExp>::fmt(&self.to_f32(), f)
92    }
93}
94
95impl Default for CssSignedNumber {
96    #[inline(always)]
97    fn default() -> Self {
98        Self::Zero
99    }
100}
101
102impl Add<CssSignedNumber> for CssSignedNumber {
103    type Output = Self;
104
105    #[inline(always)]
106    fn add(self, rhs: CssSignedNumber) -> Self::Output {
107        <Self as CssNumber>::clamp(self.to_f32() + rhs.0)
108    }
109}
110
111impl AddAssign<CssSignedNumber> for CssSignedNumber {
112    #[inline(always)]
113    fn add_assign(&mut self, rhs: CssSignedNumber) {
114        *self = self.add(rhs)
115    }
116}
117
118impl Sub<CssSignedNumber> for CssSignedNumber {
119    type Output = Self;
120
121    #[inline(always)]
122    fn sub(self, rhs: CssSignedNumber) -> Self::Output {
123        <Self as CssNumber>::clamp(self.to_f32() - rhs.0)
124    }
125}
126
127impl SubAssign<CssSignedNumber> for CssSignedNumber {
128    #[inline(always)]
129    fn sub_assign(&mut self, rhs: CssSignedNumber) {
130        *self = self.sub(rhs)
131    }
132}
133
134impl Mul<CssSignedNumber> for CssSignedNumber {
135    type Output = Self;
136
137    #[inline(always)]
138    fn mul(self, rhs: CssSignedNumber) -> Self::Output {
139        <Self as CssNumber>::clamp(self.to_f32() * rhs.0)
140    }
141}
142
143impl MulAssign<CssSignedNumber> for CssSignedNumber {
144    #[inline(always)]
145    fn mul_assign(&mut self, rhs: CssSignedNumber) {
146        *self = self.mul(rhs)
147    }
148}
149
150impl Div<CssSignedNumber> for CssSignedNumber {
151    type Output = Self;
152
153    #[inline(always)]
154    fn div(self, rhs: CssSignedNumber) -> Self::Output {
155        if rhs.0.is_nan() {
156            let value = if (self.to_f32() / rhs.0).is_sign_positive() {
157                ::std::f32::MAX
158            } else {
159                ::std::f32::MIN
160            };
161            CssSignedNumber(value)
162        } else {
163            <Self as CssNumber>::clamp(self.to_f32() / rhs.0)
164        }
165    }
166}
167
168impl DivAssign<CssSignedNumber> for CssSignedNumber {
169    #[inline(always)]
170    fn div_assign(&mut self, rhs: CssSignedNumber) {
171        *self = self.div(rhs)
172    }
173}
174
175impl Rem<CssSignedNumber> for CssSignedNumber {
176    type Output = Self;
177
178    #[inline(always)]
179    fn rem(self, rhs: CssSignedNumber) -> Self::Output {
180        if rhs.0.is_nan() {
181            let value = if (self.to_f32() % rhs.0).is_sign_positive() {
182                ::std::f32::MAX
183            } else {
184                ::std::f32::MIN
185            };
186            CssSignedNumber(value)
187        } else {
188            <Self as CssNumber>::clamp(self.to_f32() % rhs.0)
189        }
190    }
191}
192
193impl RemAssign<CssSignedNumber> for CssSignedNumber {
194    #[inline(always)]
195    fn rem_assign(&mut self, rhs: CssSignedNumber) {
196        *self = self.rem(rhs)
197    }
198}
199
200impl Neg for CssSignedNumber {
201    type Output = Self;
202
203    #[inline(always)]
204    fn neg(self) -> Self::Output {
205        if self.is_zero() {
206            self
207        } else {
208            CssSignedNumber(-self.to_f32())
209        }
210    }
211}
212
213impl CssNumberNewType<Self> for CssSignedNumber {
214    #[inline(always)]
215    fn to_f32(&self) -> f32 {
216        self.0
217    }
218
219    #[inline(always)]
220    fn as_CssNumber(&self) -> &CssSignedNumber {
221        self
222    }
223}
224
225impl Deref for CssSignedNumber {
226    type Target = f32;
227
228    #[inline(always)]
229    fn deref(&self) -> &Self::Target {
230        &self.0
231    }
232}
233
234impl From<u16> for CssSignedNumber {
235    #[inline(always)]
236    fn from(small: u16) -> CssSignedNumber {
237        CssSignedNumber(small as f32)
238    }
239}
240
241impl From<i16> for CssSignedNumber {
242    #[inline(always)]
243    fn from(small: i16) -> CssSignedNumber {
244        CssSignedNumber(small as f32)
245    }
246}
247
248impl From<u8> for CssSignedNumber {
249    #[inline(always)]
250    fn from(small: u8) -> CssSignedNumber {
251        CssSignedNumber(small as f32)
252    }
253}
254
255impl From<i8> for CssSignedNumber {
256    #[inline(always)]
257    fn from(small: i8) -> CssSignedNumber {
258        CssSignedNumber(small as f32)
259    }
260}
261
262impl FromStr for CssSignedNumber {
263    type Err = UnitFromStrError;
264
265    fn from_str(s: &str) -> Result<Self, Self::Err> {
266        let value = f32::from_str(s)?;
267        Ok(CssSignedNumber::new(value)?)
268    }
269}
270
271impl CssNumber for CssSignedNumber {
272    const Zero: Self = CssSignedNumber(0.0);
273
274    const One: Self = CssSignedNumber(1.0);
275
276    const Maximum: Self = CssSignedNumber(::std::f32::MAX);
277
278    const Minimum: Self = CssSignedNumber(::std::f32::MIN);
279
280    const DotsPerInch: Self = CssSignedNumber(96.0);
281
282    const CentimetresPerInch: Self = CssSignedNumber(2.54);
283
284    #[inline(always)]
285    fn as_f32(&self) -> f32 {
286        self.0
287    }
288
289    #[inline(always)]
290    fn as_u32(&self) -> u32 {
291        self.0 as u32
292    }
293
294    #[doc(hidden)]
295    #[inline(always)]
296    fn _construct(value: f32) -> Self {
297        CssSignedNumber(value)
298    }
299
300    #[inline(always)]
301    fn parseNumber<'i>(
302        value: f32,
303        _int_value: Option<i32>,
304    ) -> Result<Self, ParseError<'i, CustomParseError<'i>>> {
305        CssSignedNumber::new(value).map_err(|cssNumberConversionError| {
306            ParseError::from(CustomParseError::CouldNotParseCssSignedNumber(
307                cssNumberConversionError,
308                value,
309            ))
310        })
311    }
312}
313
314impl AppUnitsPer for CssSignedNumber {
315    /// Number of app units per pixel
316    const AppUnitsPerPX: Self = CssSignedNumber(f32::AppUnitsPerPX);
317
318    /// Number of app units per inch
319    const AppUnitsPerIN: Self = CssSignedNumber(f32::AppUnitsPerIN);
320
321    /// Number of app units per centimeter
322    const AppUnitsPerCM: Self = CssSignedNumber(f32::AppUnitsPerCM);
323
324    /// Number of app units per millimeter
325    const AppUnitsPerMM: Self = CssSignedNumber(f32::AppUnitsPerMM);
326
327    /// Number of app units per quarter
328    const AppUnitsPerQ: Self = CssSignedNumber(f32::AppUnitsPerQ);
329
330    /// Number of app units per point
331    const AppUnitsPerPT: Self = CssSignedNumber(f32::AppUnitsPerPT);
332
333    /// Number of app units per pica
334    const AppUnitsPerPC: Self = CssSignedNumber(f32::AppUnitsPerPC);
335}
336
337impl Unit for CssSignedNumber {
338    type Number = Self;
339
340    const HasDimension: bool = false;
341
342    #[inline(always)]
343    fn parse_one_outside_calc_function<'i, 't>(
344        context: &ParserContext,
345        input: &mut Parser<'i, 't>,
346    ) -> Result<
347        CalculablePropertyValue<Self>,
348        ParseError<'i, CustomParseError<'i>>,
349    > {
350        let functionParser = match *input.next()? {
351            Token::Number {
352                value, int_value, ..
353            } => return Self::parseNumber(value, int_value).map(Constant),
354
355            Token::Function(ref name) => FunctionParser::parser(name)?,
356
357            ref unexpectedToken => {
358                return CustomParseError::unexpectedToken(unexpectedToken)
359            }
360        };
361        functionParser.parse_one_outside_calc_function(context, input)
362    }
363
364    #[inline(always)]
365    fn parse_one_inside_calc_function<'i, 't>(
366        context: &ParserContext,
367        input: &mut Parser<'i, 't>,
368    ) -> Result<
369        Either<CalculablePropertyValue<Self>, CalcExpression<Self>>,
370        ParseError<'i, CustomParseError<'i>>,
371    > {
372        let functionParser = match *input.next()? {
373            Token::Number {
374                value, int_value, ..
375            } => {
376                return Self::parseNumber(value, int_value)
377                    .map(|value| Left(Constant(value)))
378            }
379
380            Token::Percentage { unit_value, .. } => {
381                return PercentageUnit::parse_percentage(unit_value)
382                    .map(|value| Left(Percentage(value)))
383            }
384
385            Token::ParenthesisBlock => FunctionParser::parentheses,
386
387            Token::Function(ref name) => FunctionParser::parser(name)?,
388
389            ref unexpectedToken => {
390                return CustomParseError::unexpectedToken(unexpectedToken)
391            }
392        };
393        functionParser.parse_one_inside_calc_function(context, input)
394    }
395
396    #[inline(always)]
397    fn to_canonical_dimension_value<
398        Conversion: FontRelativeLengthConversion<Self::Number>
399            + ViewportPercentageLengthConversion<Self::Number>
400            + PercentageConversion<Self::Number>,
401    >(
402        &self,
403        _conversion: &Conversion,
404    ) -> Self::Number {
405        self.to_CssNumber()
406    }
407
408    #[inline(always)]
409    fn from_raw_css_for_var_expression_evaluation(
410        value: &str,
411        _is_not_in_page_rule: bool,
412    ) -> Option<Self> {
413        fn from_raw_css_for_var_expression_evaluation_internal<'i: 't, 't>(
414            input: &mut Parser<'i, 't>,
415        ) -> Result<CssSignedNumber, ParseError<'i, CustomParseError<'i>>>
416        {
417            let value = match *input.next()? {
418                Token::Number {
419                    value, int_value, ..
420                } => CssSignedNumber::parseNumber(value, int_value),
421
422                ref unexpectedToken => {
423                    CustomParseError::unexpectedToken(unexpectedToken)
424                }
425            };
426
427            input.skip_whitespace();
428
429            input.expect_exhausted()?;
430
431            value
432        }
433
434        const LineNumberingIsZeroBased: u32 = 0;
435
436        let mut parserInput = ParserInput::new_with_line_number_offset(
437            value,
438            LineNumberingIsZeroBased,
439        );
440        let mut input = Parser::new(&mut parserInput);
441
442        from_raw_css_for_var_expression_evaluation_internal(&mut input).ok()
443    }
444}