1use super::prelude::*;
2use crate::{Exact, Flex, Percentage};
3
4macro_rules! apply_lengths {
5 ($ident: ident) => {
6 $ident! {
7 Em,
9 Rem,
10 Ex,
11 Rex,
12 Cap,
13 Rcap,
14 Ch,
15 Rch,
16 Ic,
17 Ric,
18 Lh,
19 Rlh,
20
21 Vw,
23 Svw,
24 Lvw,
25 Dvw,
26 Vh,
27 Svh,
28 Lvh,
29 Dvh,
30 Vi,
31 Svi,
32 Lvi,
33 Dvi,
34 Vb,
35 Svb,
36 Lvb,
37 Dvb,
38 Vmin,
39 Svmin,
40 Lvmin,
41 Dvmin,
42 Vmax,
43 Svmax,
44 Lvmax,
45 Dvmax,
46
47 Cm,
49 Mm,
50 Q,
51 In,
52 Pc,
53 Pt,
54 Px,
55
56 Cqw,
58 Cqh,
59 Cqi,
60 Cqb,
61 Cqmin,
62 Cqmax,
63 }
64 };
65}
66
67macro_rules! define_length {
68 ( $($name: ident),+ $(,)* ) => {
69 #[derive(Parse, Peek, IntoCursor, ToSpan, SemanticEq, ToCursors, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
70 #[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
71 #[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
72 #[derive(csskit_derives::NodeWithMetadata)]
73 #[metadata(node_kinds = Dimension)]
74 pub enum Length {
75 Zero(Exact<T![Number], 0>),
76 $(
77 #[atom(CssAtomSet::$name)]
78 $name(T![Dimension]),
79 )+
80 }
81 }
82}
83apply_lengths!(define_length);
84
85impl Length {
86 const PX_CM: f32 = Self::PX_IN / 2.54;
87 const PX_MM: f32 = Self::PX_IN / 25.4;
88 const PX_Q: f32 = Self::PX_MM / 4.0;
89 const PX_IN: f32 = 96.0;
90 const PX_PC: f32 = Self::PX_IN / 6.0;
91 const PX_PT: f32 = Self::PX_IN / 72.0;
92
93 pub fn to_px(&self) -> Option<f32> {
94 match self {
95 Self::Zero(_) => Some(0.0),
96 Self::Cm(d) => Some(Into::<f32>::into(*d) * Self::PX_CM),
97 Self::Mm(d) => Some(Into::<f32>::into(*d) * Self::PX_MM),
98 Self::Q(d) => Some(Into::<f32>::into(*d) * Self::PX_Q),
99 Self::In(d) => Some(Into::<f32>::into(*d) * Self::PX_IN),
100 Self::Pc(d) => Some(Into::<f32>::into(*d) * Self::PX_PC),
101 Self::Pt(d) => Some(Into::<f32>::into(*d) * Self::PX_PT),
102 _ => None,
103 }
104 }
105}
106
107impl From<Length> for f32 {
108 fn from(val: Length) -> Self {
109 macro_rules! match_length {
110 ( $($name: ident),+ $(,)* ) => {
111 match val {
112 Length::Zero(_) => 0.0,
113 $(Length::$name(f) => f.into()),+
114 }
115 }
116 }
117 apply_lengths!(match_length)
118 }
119}
120
121impl PartialEq<f32> for Length {
122 fn eq(&self, other: &f32) -> bool {
123 let f: f32 = (*self).into();
124 f == *other
125 }
126}
127
128impl ToNumberValue for Length {
129 fn to_number_value(&self) -> Option<f32> {
130 Some((*self).into())
131 }
132}
133
134#[derive(
135 Parse, Peek, IntoCursor, ToSpan, SemanticEq, ToCursors, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
136)]
137#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
138#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
139#[derive(csskit_derives::NodeWithMetadata)]
140pub enum LengthPercentage {
141 #[cfg_attr(feature = "visitable", visit(skip))]
142 Zero(Exact<T![Number], 0>),
143 Length(Length),
144 #[cfg_attr(feature = "visitable", visit(skip))]
145 Percent(Percentage),
146}
147
148impl From<LengthPercentage> for f32 {
149 fn from(val: LengthPercentage) -> Self {
150 match val {
151 LengthPercentage::Zero(_) => 0.0,
152 LengthPercentage::Percent(f) => f.into(),
153 LengthPercentage::Length(f) => f.into(),
154 }
155 }
156}
157
158impl ToNumberValue for LengthPercentage {
159 fn to_number_value(&self) -> Option<f32> {
160 Some((*self).into())
161 }
162}
163
164#[derive(
165 Parse, Peek, IntoCursor, ToSpan, SemanticEq, ToCursors, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
166)]
167#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
168#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
169#[derive(csskit_derives::NodeWithMetadata)]
170pub enum LengthPercentageNumber {
171 Length(Length),
172 #[cfg_attr(feature = "visitable", visit(skip))]
173 Percent(Percentage),
174 #[cfg_attr(feature = "visitable", visit(skip))]
175 Number(T![Number]),
176}
177
178impl From<LengthPercentageNumber> for f32 {
179 fn from(val: LengthPercentageNumber) -> Self {
180 match val {
181 LengthPercentageNumber::Length(f) => f.into(),
182 LengthPercentageNumber::Percent(f) => f.into(),
183 LengthPercentageNumber::Number(f) => f.into(),
184 }
185 }
186}
187
188impl ToNumberValue for LengthPercentageNumber {
189 fn to_number_value(&self) -> Option<f32> {
190 Some((*self).into())
191 }
192}
193
194#[derive(
195 Parse, Peek, IntoCursor, ToSpan, SemanticEq, ToCursors, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
196)]
197#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
198#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(children))]
199#[derive(csskit_derives::NodeWithMetadata)]
200pub enum LengthPercentageOrFlex {
201 Flex(Flex),
202 LengthPercentage(LengthPercentage),
203}
204
205impl From<LengthPercentageOrFlex> for f32 {
206 fn from(val: LengthPercentageOrFlex) -> Self {
207 match val {
208 LengthPercentageOrFlex::Flex(f) => f.into(),
209 LengthPercentageOrFlex::LengthPercentage(l) => l.into(),
210 }
211 }
212}
213
214impl ToNumberValue for LengthPercentageOrFlex {
215 fn to_number_value(&self) -> Option<f32> {
216 Some((*self).into())
217 }
218}
219
220#[derive(
221 Parse, Peek, ToCursors, IntoCursor, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
222)]
223#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
224#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
225#[derive(csskit_derives::NodeWithMetadata)]
226pub enum NumberLength {
227 #[cfg_attr(feature = "visitable", visit(skip))]
228 Number(T![Number]),
229 Length(Length),
230}
231
232impl From<NumberLength> for f32 {
233 fn from(val: NumberLength) -> Self {
234 match val {
235 NumberLength::Number(n) => n.into(),
236 NumberLength::Length(n) => n.into(),
237 }
238 }
239}
240
241impl ToNumberValue for NumberLength {
242 fn to_number_value(&self) -> Option<f32> {
243 Some((*self).into())
244 }
245}
246
247#[cfg(test)]
248mod tests {
249 use super::*;
250 use crate::CssAtomSet;
251 use css_parse::assert_parse;
252
253 #[test]
254 fn size_test() {
255 assert_eq!(std::mem::size_of::<Length>(), 16);
256 assert_eq!(std::mem::size_of::<LengthPercentage>(), 16);
257 assert_eq!(std::mem::size_of::<NumberLength>(), 16);
258 }
259
260 #[test]
261 fn test_writes() {
262 assert_parse!(CssAtomSet::ATOMS, Length, "10px");
263 assert_parse!(CssAtomSet::ATOMS, Length, "1.2345679px");
265 assert_parse!(CssAtomSet::ATOMS, Length, "-1px");
267 assert_parse!(CssAtomSet::ATOMS, LengthPercentage, "1%");
269 }
270}