rsass/value/
unit.rs

1//! The Unit enum defines css units
2
3use std::f64::consts::FRAC_1_PI;
4use std::fmt;
5
6/// Units in css.
7///
8/// As defined in <https://www.w3.org/TR/css3-values/>
9#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
10pub enum Unit {
11    /// `em` unit, lengths in em-like dimension.
12    Em,
13    /// `ex` unit, lengths in em-like dimension.
14    Ex,
15    /// `ch` unit, lengths in em-like dimension.
16    Ch,
17    /// `ch` unit, lengths in rem-like dimension.
18    Rem,
19    /// `vw` unit, length relative to viewport width.
20    Vw,
21    /// `vh` unit, length relative to viewport height.
22    Vh,
23    /// `vmin` unit, length relative to min viewport size.
24    Vmin,
25    /// `vmax` unit, length relative to max viewport size.
26    Vmax,
27    /// `cm` unit, absolute length.
28    Cm,
29    /// `mm` unit, absolute length.
30    Mm,
31    /// `q` unit, absolute length (4Q == 1mm).
32    Q,
33    /// `in` unit, absolute length in inch.
34    In,
35    /// `pt` unit, absolute length (72pt == 1in).
36    Pt,
37    /// `pc`unit, absolute length (1pc == 12pt, 6pc == 1in).
38    Pc,
39    /// `px`unit, originally pixel size, but does not really mean anything now.
40    Px,
41
42    /// `deg` unit, angle in degrees (360 to a turn).
43    Deg,
44    /// `grad` unit, angle in grad (400 to a turn).
45    Grad,
46    /// `rad` unit, angle in degrees (2pi to a turn).
47    Rad,
48    /// `turn` unit, angle in turns.
49    Turn,
50
51    /// `s` unit, time in seconds.
52    S,
53    /// `ms` unit, time in milliseconds.
54    Ms,
55    /// `hz` unit, frequency in Hz.
56    Hz,
57    /// `khz` unit, frequency in kHz.
58    Khz,
59
60    /// `dpi` unit, resolution in dots per inch.
61    Dpi,
62    /// `dpcm` unit, resolution in dots per cm.
63    Dpcm,
64    /// `dppx` unit, resolution in dots per px unit.
65    Dppx,
66
67    /// `%` unit, a percentage of something.
68    Percent,
69    /// `fr` unit, for grid-relative lengths.
70    Fr,
71    /// No unit.
72    None,
73
74    /// An unknown (but named) unit.
75    Unknown(String),
76}
77
78/// Dimension of a unit.
79///
80/// Units of the same dimension can be converted to each other.
81/// There are multiple "length" dimensions, since font-based,
82/// window-based and absolute lengths can't be converted to each
83/// other.
84///
85/// This type is for compatibility in sass functions.
86/// See also [`CssDimension`].
87#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
88pub enum Dimension {
89    /// An absolute length, can be converted to metric.
90    LengthAbs,
91    /// A length relative to viewport width.
92    LengthVw,
93    /// A length relative to viewport height.
94    LengthVh,
95    /// A length relative to viewport size (min or max).
96    LengthVx,
97    /// A length relatvie to base font size.
98    LengthRem,
99    /// A length relative to font size.
100    LenghtEm,
101    /// An angle.
102    Angle,
103    /// A duration.
104    Time,
105    /// A frequency.
106    Frequency,
107    /// A resolution (number of pixels per length).
108    Resolution,
109    /// No dimension (no unit, percentage, or grid fraction).
110    None,
111    /// The dimension of an unknown (but named) unit.
112    Unknown(String),
113}
114
115impl Unit {
116    /// Get the dimension of this unit.
117    pub fn dimension(&self) -> Dimension {
118        match *self {
119            Self::Cm
120            | Self::Mm
121            | Self::Q
122            | Self::In
123            | Self::Pc
124            | Self::Pt
125            | Self::Px => Dimension::LengthAbs,
126
127            Self::Vw => Dimension::LengthVw,
128            Self::Vh => Dimension::LengthVh,
129            Self::Vmin | Self::Vmax => Dimension::LengthVx,
130            Self::Ch | Self::Em | Self::Ex => Dimension::LenghtEm,
131            Self::Rem => Dimension::LengthRem,
132
133            Self::Deg | Self::Grad | Self::Rad | Self::Turn => {
134                Dimension::Angle
135            }
136
137            Self::S | Self::Ms => Dimension::Time,
138
139            Self::Hz | Self::Khz => Dimension::Frequency,
140
141            Self::Dpi | Self::Dpcm | Self::Dppx => Dimension::Resolution,
142
143            Self::Percent | Self::Fr | Self::None => Dimension::None,
144
145            Self::Unknown(ref name) => Dimension::Unknown(name.clone()),
146        }
147    }
148
149    /// Get a scaling factor to convert this unit to another unit.
150    ///
151    /// Returns None if the units are of different dimension.
152    pub fn scale_to(&self, other: &Self) -> Option<f64> {
153        if self == other {
154            Some(1.)
155        } else if self.dimension() == other.dimension() {
156            Some(self.scale_factor() / other.scale_factor())
157        } else {
158            None
159        }
160    }
161
162    /// Some of these are exact and correct, others are more arbitrary.
163    /// When comparing 10cm to 4in, these factors will give correct results.
164    /// When comparing rems to vw, who can say?
165    pub(crate) fn scale_factor(&self) -> f64 {
166        #[allow(clippy::match_same_arms)]
167        match *self {
168            Self::Em | Self::Rem => 5.,
169            Self::Ex => 3.,
170            Self::Ch => 2.,
171            Self::Vw | Self::Vh | Self::Vmin | Self::Vmax => 1.,
172            Self::Cm => 10.,
173            Self::Mm => 1.,
174            Self::Q => 1. / 4.,
175            Self::In => 254. / 10.,
176            Self::Pt => 254. / 720.,
177            Self::Pc => 254. / 60.,
178            Self::Px => 254. / 960.,
179
180            Self::Deg => 1. / 360.,
181            Self::Grad => 1. / 400.,
182            Self::Rad => FRAC_1_PI / 2.0,
183            Self::Turn => 1.,
184
185            Self::S => 1.,
186            Self::Ms => 1. / 1000.,
187
188            Self::Hz => 1.,
189            Self::Khz => 1000.,
190
191            Self::Dpi => 1. / 96.,
192            Self::Dpcm => 254. / 9600.,
193            Self::Dppx => 1.,
194
195            Self::Percent => 1. / 100.,
196            Self::Fr => 1.,
197            Self::None => 1.,
198
199            Self::Unknown(_) => 1.,
200        }
201    }
202}
203
204impl fmt::Display for Unit {
205    fn fmt(&self, out: &mut fmt::Formatter) -> fmt::Result {
206        match *self {
207            // Distance units, <length> type
208            Self::Em => write!(out, "em"),
209            Self::Ex => write!(out, "ex"),
210            Self::Ch => write!(out, "ch"),
211            Self::Rem => write!(out, "rem"),
212            Self::Vw => write!(out, "vw"),
213            Self::Vh => write!(out, "vh"),
214            Self::Vmin => write!(out, "vmin"),
215            Self::Vmax => write!(out, "vmax"),
216            Self::Cm => write!(out, "cm"),
217            Self::Mm => write!(out, "mm"),
218            Self::Q => write!(out, "Q"),
219            Self::In => write!(out, "in"),
220            Self::Pt => write!(out, "pt"),
221            Self::Pc => write!(out, "pc"),
222            Self::Px => write!(out, "px"),
223            // <angle> type
224            Self::Deg => write!(out, "deg"),
225            Self::Grad => write!(out, "grad"),
226            Self::Rad => write!(out, "rad"),
227            Self::Turn => write!(out, "turn"),
228            // <time> type
229            Self::S => write!(out, "s"),
230            Self::Ms => write!(out, "ms"),
231            // <frequency> type
232            Self::Hz => write!(out, "Hz"),
233            Self::Khz => write!(out, "kHz"),
234            // <resolution>
235            Self::Dpi => write!(out, "dpi"),
236            Self::Dpcm => write!(out, "dpcm"),
237            Self::Dppx => write!(out, "dppx"),
238            // Special units
239            Self::Percent => write!(out, "%"),
240            Self::Fr => write!(out, "fr"),
241            Self::None => Ok(()),
242
243            Self::Unknown(ref name) => out.write_str(name),
244        }
245    }
246}
247
248/// Dimension of a unit.
249///
250/// Units of the same dimension can be converted to each other.
251/// There is a single "length" dimension, since all lengths _can_ be
252/// converted to each other in the browser, where all are converted to
253/// device pixels anyway.
254///
255/// This type is for compatibility in css functions.
256/// See also [`Dimension`].
257#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
258pub enum CssDimension {
259    /// A length of any kind.
260    Length,
261    /// An angle.
262    Angle,
263    /// A duration.
264    Time,
265    /// A frequency.
266    Frequency,
267    /// A resolution (number of pixels per length).
268    Resolution,
269    /// No dimension (no unit, percentage, or grid fraction).
270    None,
271    /// The dimension of an unknown (but named) unit.
272    Unknown(String),
273}
274
275impl From<Dimension> for CssDimension {
276    fn from(dim: Dimension) -> Self {
277        match dim {
278            Dimension::LengthAbs
279            | Dimension::LengthVw
280            | Dimension::LengthVh
281            | Dimension::LengthVx
282            | Dimension::LengthRem
283            | Dimension::LenghtEm => Self::Length,
284            Dimension::Angle => Self::Angle,
285            Dimension::Time => Self::Time,
286            Dimension::Frequency => Self::Frequency,
287            Dimension::Resolution => Self::Resolution,
288            Dimension::None => Self::None,
289            Dimension::Unknown(s) => Self::Unknown(s),
290        }
291    }
292}