Skip to main content

whisker_css/data_type/
number.rs

1//! `<number>` — a real number.
2//!
3//! Lynx reference: <https://lynxjs.org/api/css/data-type/number.html>
4//!
5//! Lynx accepts integers, decimals, and scientific notation (`10e3`,
6//! `-1.2`, `0.5`). The literals `12.` (trailing dot) and `.5e` are
7//! rejected by the Lynx parser; constructing [`Number`] from `f32`
8//! is unrestricted because the value is re-serialized through
9//! [`write_number`](crate::to_css::write_number), which always
10//! emits a well-formed literal.
11
12use core::fmt;
13
14use crate::to_css::{write_number, ToCss};
15
16/// A CSS `<number>` value.
17///
18/// Wraps `f32` so call sites carry intent (`Number(1.0)` vs. a bare
19/// `1.0` which could be a percentage, an opacity, or a flex factor).
20#[derive(Copy, Clone, Debug, PartialEq)]
21pub struct Number(pub f32);
22
23impl Number {
24    /// Construct from a primitive `f32`.
25    pub const fn new(v: f32) -> Self {
26        Self(v)
27    }
28
29    /// Underlying value.
30    pub const fn value(self) -> f32 {
31        self.0
32    }
33}
34
35impl From<f32> for Number {
36    fn from(v: f32) -> Self {
37        Self(v)
38    }
39}
40
41impl From<i32> for Number {
42    fn from(v: i32) -> Self {
43        Self(v as f32)
44    }
45}
46
47impl ToCss for Number {
48    fn to_css(&self, dest: &mut dyn fmt::Write) -> fmt::Result {
49        write_number(dest, self.0)
50    }
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56
57    #[test]
58    fn integer_serializes_without_decimal() {
59        assert_eq!(Number(1.0).to_css_string(), "1");
60        assert_eq!(Number(-3.0).to_css_string(), "-3");
61        assert_eq!(Number(0.0).to_css_string(), "0");
62    }
63
64    #[test]
65    fn fractional_serializes_with_decimal() {
66        assert_eq!(Number(0.5).to_css_string(), "0.5");
67        assert_eq!(Number(-1.25).to_css_string(), "-1.25");
68    }
69
70    #[test]
71    fn from_impls() {
72        assert_eq!(Number::from(2_i32), Number(2.0));
73        assert_eq!(Number::from(2.5_f32), Number(2.5));
74    }
75
76    #[test]
77    fn accessors() {
78        let n = Number::new(7.0);
79        assert_eq!(n.value(), 7.0);
80    }
81}