Skip to main content

math_core_renderer_internal/
length.rs

1//! Lengths, both absolute and relative to font size.
2
3// use std::fmt::Write;
4
5#[cfg(feature = "serde")]
6use serde::Serialize;
7use strum_macros::IntoStaticStr;
8
9#[derive(Debug, Clone, Copy, PartialEq, IntoStaticStr)]
10#[cfg_attr(feature = "serde", derive(Serialize))]
11pub enum LengthUnit {
12    // absolute unit
13    #[strum(serialize = "rem")]
14    Rem,
15    // relative units
16    #[strum(serialize = "em")]
17    Em,
18    #[strum(serialize = "ex")]
19    Ex,
20}
21
22#[derive(Debug, Clone, Copy, PartialEq)]
23#[cfg_attr(feature = "serde", derive(Serialize))]
24pub struct Length {
25    value: LengthValue,
26    pub(crate) unit: LengthUnit,
27}
28
29#[derive(Debug, Clone, Copy, PartialEq)]
30#[cfg_attr(feature = "serde", derive(Serialize))]
31#[repr(transparent)]
32#[cfg_attr(feature = "serde", serde(transparent))]
33pub struct LengthValue(pub(crate) f32);
34
35impl Length {
36    pub const fn new(value: f32, unit: LengthUnit) -> Self {
37        Length {
38            value: LengthValue(value),
39            unit,
40        }
41    }
42
43    pub fn push_to_string(&self, output: &mut String) {
44        let mut buffer = dtoa::Buffer::new();
45        let result = buffer.format(self.value.0);
46        // let _ = write!(output, "{}", self.value.0).is_ok();
47        output.push_str(result.strip_suffix(".0").unwrap_or(result));
48        if self.value.0 != 0.0 {
49            output.push_str(<&'static str>::from(self.unit));
50        }
51    }
52
53    pub const fn none() -> Self {
54        Length {
55            value: LengthValue(f32::NAN),
56            unit: LengthUnit::Rem,
57        }
58    }
59
60    pub const fn zero() -> Self {
61        Length {
62            value: LengthValue(0.0),
63            unit: LengthUnit::Rem,
64        }
65    }
66
67    pub const fn into_parts(self) -> (LengthValue, LengthUnit) {
68        (self.value, self.unit)
69    }
70
71    pub fn from_parts(value: LengthValue, unit: LengthUnit) -> Option<Self> {
72        if value.0.is_finite() {
73            Some(Length { value, unit })
74        } else {
75            None
76        }
77    }
78
79    pub const fn is_negative(self) -> bool {
80        self.value.0 < 0.0
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87    use LengthUnit::*;
88
89    #[test]
90    fn test_write() {
91        let mut output = String::new();
92        Length::new(0.0, Rem).push_to_string(&mut output);
93        assert_eq!(&output, "0");
94        output.clear();
95        Length::new(1.0, Rem).push_to_string(&mut output);
96        assert_eq!(&output, "1rem");
97        output.clear();
98        Length::new(10.0, Rem).push_to_string(&mut output);
99        assert_eq!(&output, "10rem");
100        output.clear();
101        Length::new(5965232.0, Rem).push_to_string(&mut output);
102        assert_eq!(&output, "5965232rem");
103        output.clear();
104        Length::new(-5965232.0, Rem).push_to_string(&mut output);
105        assert_eq!(&output, "-5965232rem");
106    }
107
108    #[test]
109    fn test_write_relative() {
110        let mut output = String::new();
111        Length::new(0.0, Em).push_to_string(&mut output);
112        assert_eq!(&output, "0");
113        output.clear();
114        Length::new(0.0, Ex).push_to_string(&mut output);
115        assert_eq!(&output, "0");
116        output.clear();
117        Length::new(1.0, Em).push_to_string(&mut output);
118        assert_eq!(&output, "1em");
119        output.clear();
120        Length::new(1.0, Ex).push_to_string(&mut output);
121        assert_eq!(&output, "1ex");
122        output.clear();
123        Length::new(546.0, Em).push_to_string(&mut output);
124        assert_eq!(&output, "546em");
125        output.clear();
126        Length::new(546.0, Ex).push_to_string(&mut output);
127        assert_eq!(&output, "546ex");
128        output.clear();
129        Length::new(-546.0, Em).push_to_string(&mut output);
130        assert_eq!(&output, "-546em");
131        output.clear();
132        Length::new(-546.0, Ex).push_to_string(&mut output);
133        assert_eq!(&output, "-546ex");
134        output.clear();
135    }
136}