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}