elvis_core/value/
unit.rs

1//! unit system
2use crate::Error;
3use std::{cmp::Ordering, str::FromStr};
4
5/// Follows [CSS Values 3][1] drafted in [csswg.org][2].
6///
7/// ## Absolute Lengths
8/// | unit | name                | equivalence          |
9/// |------|---------------------|----------------------|
10/// | cm   | centermeters        | 1cm = 96px/2.54      |
11/// | mm   | millimeters         | 1mm == 1/10th of 1cm |
12/// | Q    | quarter-millimeters | 1Q = 1/40th of 1cm   |
13/// | in   | inches              | 1in = 2.54cm = 96px  |
14/// | pc   | picas               | 1pc = 1/6th of 1in   |
15/// | pt   | points              | 1pt = 1/72th of 1in  |
16/// | px   | pixels              | 1px = 1/96th of 1in  |
17///
18/// ## Relative Lengths
19/// | unit | relative to                                                 |
20/// |------|-------------------------------------------------------------|
21/// | em   | font size of element                                        |
22/// | ex   | x-height of element's font                                  |
23/// | ch   | width of the "0" (ZERO, U+0030) glyph in the element’s font |
24/// | rem  | font size of the root element                               |
25/// | vw   | 1% of viewport’s width                                      |
26/// | vh   | 1% of viewport’s height                                     |
27/// | vmin | 1% of viewport’s smaller dimension                          |
28/// | vmax | 1% of viewport’s larger dimension                           |
29///
30/// ## Others
31/// | unit | represents                                                                      |
32/// |------|---------------------------------------------------------------------------------|
33/// | dpi  | Dots per inch                                                                   |
34/// | dpcm | Dots per centmeter                                                              |
35/// | dppx | Dots per px unit                                                                |
36/// | fr   | This unit represents one fraction of the available space in the grid container. |
37///
38/// [1]: https://drafts.csswg.org/css-values-3
39/// [2]: https://drafts.csswg.org
40#[derive(Clone, Copy, Debug)]
41pub enum Unit {
42    /// auto size
43    Auto,
44    /// ch
45    Ch(f64),
46    /// cm
47    Cm(f64),
48    /// dpi
49    Dpi(f64),
50    /// dpcm
51    Dpcm(f64),
52    /// dppx
53    Dppx(f64),
54    /// em
55    Em(f64),
56    /// fr
57    Fr(f64),
58    /// in
59    In(f64),
60    /// mm
61    Mm(f64),
62    /// pc
63    Pc(f64),
64    /// pt
65    Pt(f64),
66    /// px
67    Px(f64),
68    /// q
69    Q(f64),
70    /// rem
71    Rem(f64),
72    /// vh
73    Vh(f64),
74    /// vmax
75    Vmax(f64),
76    /// vmin
77    Vmin(f64),
78    /// vw
79    Vw(f64),
80    /// v%
81    Percent(f64),
82    /// no unit
83    None(f64),
84}
85
86impl Eq for Unit {}
87
88impl PartialEq for Unit {
89    fn eq(&self, o: &Self) -> bool {
90        self.to_string().eq(&o.to_string())
91    }
92}
93
94impl Ord for Unit {
95    fn cmp(&self, _: &Self) -> Ordering {
96        Ordering::Equal
97    }
98}
99
100impl PartialOrd for Unit {
101    fn partial_cmp(&self, _: &Self) -> Option<Ordering> {
102        None
103    }
104}
105
106impl Default for Unit {
107    fn default() -> Unit {
108        Unit::Auto
109    }
110}
111
112impl FromStr for Unit {
113    type Err = Error;
114
115    fn from_str(s: &str) -> Result<Unit, Self::Err> {
116        let t = s.trim();
117        let u = t
118            .find(|c: char| !c.is_numeric() && !c.eq(&'.'))
119            .unwrap_or(0);
120
121        let v: f64 = t[..u]
122            .trim()
123            .parse()
124            .unwrap_or_else(|_| t[u..].trim().parse().unwrap_or(1.0));
125
126        Ok(match t[u..].trim().to_ascii_lowercase().as_str() {
127            "auto" | "inherit" => Unit::Auto,
128            "ch" => Unit::Ch(v),
129            "cm" => Unit::Cm(v),
130            "dpcm" => Unit::Dpcm(v),
131            "dpi" => Unit::Dpi(v),
132            "dppx" => Unit::Dppx(v),
133            "em" => Unit::Em(v),
134            "fr" => Unit::Fr(v),
135            "in" => Unit::In(v),
136            "mm" => Unit::Mm(v),
137            "pc" => Unit::Pc(v),
138            "pt" => Unit::Pt(v),
139            "px" => Unit::Px(v),
140            "q" => Unit::Q(v),
141            "rem" => Unit::Rem(v),
142            "vh" => Unit::Vh(v),
143            "vmax" => Unit::Vmax(v),
144            "vmin" => Unit::Vmin(v),
145            "vw" => Unit::Vw(v),
146            "%" => Unit::Percent(t[..u].parse().unwrap_or(100.0)),
147            _ => Unit::None(v),
148        })
149    }
150}
151
152impl ToString for Unit {
153    fn to_string(&self) -> String {
154        match self {
155            Unit::Auto => "auto".into(),
156            Unit::Ch(n) => format!("{:.1}ch", n),
157            Unit::Cm(n) => format!("{:.1}cm", n),
158            Unit::Dpcm(n) => format!("{:.1}dpcm", n),
159            Unit::Dpi(n) => format!("{:.1}dpi", n),
160            Unit::Dppx(n) => format!("{:.1}dppx", n),
161            Unit::Em(n) => format!("{:.1}em", n),
162            Unit::Fr(n) => format!("{:.1}fr", n),
163            Unit::In(n) => format!("{:.1}in", n),
164            Unit::Mm(n) => format!("{:.1}mm", n),
165            Unit::Pc(n) => format!("{:.1}pc", n),
166            Unit::Pt(n) => format!("{:.1}pt", n),
167            Unit::Px(n) => format!("{:.1}px", n),
168            Unit::Q(n) => format!("{:.1}Q", n),
169            Unit::Rem(n) => format!("{:.1}rem", n),
170            Unit::Vh(n) => format!("{:.1}vh", n),
171            Unit::Vmax(n) => format!("{:.1}vmax", n),
172            Unit::Vmin(n) => format!("{:.1}vmin", n),
173            Unit::Vw(n) => format!("{:.1}vw", n),
174            Unit::Percent(n) => format!("{:.1}%", n),
175            Unit::None(n) => format!("{:.0}", n),
176        }
177    }
178}
179
180/// Vec Unit
181#[derive(Clone, Eq, PartialEq, Ord, PartialOrd)]
182pub struct VecUnit(pub Vec<Unit>);
183
184impl Default for VecUnit {
185    fn default() -> VecUnit {
186        VecUnit(vec![Unit::None(0.0)])
187    }
188}
189
190impl ToString for VecUnit {
191    fn to_string(&self) -> String {
192        self.0
193            .iter()
194            .map(|u| u.to_string())
195            .collect::<Vec<String>>()
196            .join(" ")
197    }
198}
199
200impl From<Vec<Unit>> for VecUnit {
201    fn from(vu: Vec<Unit>) -> VecUnit {
202        VecUnit(vu)
203    }
204}