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 #[node]
70 #[derive(Parse, Peek, IntoCursor, ToSpan, SemanticEq, ToCursors, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
71 #[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
72 #[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
73 #[derive(csskit_derives::NodeWithMetadata)]
74 #[metadata(node_kinds = Dimension, value_kinds = Length)]
75 pub enum Length {
76 Zero(Exact<T![Number], 0>),
77 $(
78 #[atom(CssAtomSet::$name)]
79 $name(T![Dimension]),
80 )+
81 }
82 }
83}
84apply_lengths!(define_length);
85
86impl Length {
87 const PX_CM: f32 = Self::PX_IN / 2.54;
88 const PX_MM: f32 = Self::PX_IN / 25.4;
89 const PX_Q: f32 = Self::PX_MM / 4.0;
90 const PX_IN: f32 = 96.0;
91 const PX_PC: f32 = Self::PX_IN / 6.0;
92 const PX_PT: f32 = Self::PX_IN / 72.0;
93
94 pub fn to_px(&self) -> Option<f32> {
95 match self {
96 Self::Zero(_) => Some(0.0),
97 Self::Cm(d) => Some(Into::<f32>::into(*d) * Self::PX_CM),
98 Self::Mm(d) => Some(Into::<f32>::into(*d) * Self::PX_MM),
99 Self::Q(d) => Some(Into::<f32>::into(*d) * Self::PX_Q),
100 Self::In(d) => Some(Into::<f32>::into(*d) * Self::PX_IN),
101 Self::Pc(d) => Some(Into::<f32>::into(*d) * Self::PX_PC),
102 Self::Pt(d) => Some(Into::<f32>::into(*d) * Self::PX_PT),
103 _ => None,
104 }
105 }
106}
107
108impl From<Length> for f32 {
109 fn from(val: Length) -> Self {
110 macro_rules! match_length {
111 ( $($name: ident),+ $(,)* ) => {
112 match val {
113 Length::Zero(_) => 0.0,
114 $(Length::$name(f) => f.into()),+
115 }
116 }
117 }
118 apply_lengths!(match_length)
119 }
120}
121
122impl PartialEq<f32> for Length {
123 fn eq(&self, other: &f32) -> bool {
124 let f: f32 = (*self).into();
125 f == *other
126 }
127}
128
129impl ToNumberValue for Length {
130 fn to_number_value(&self) -> Option<f32> {
131 Some((*self).into())
132 }
133}
134
135impl ToNormalisedValue for Length {
136 fn to_normalised_value(&self) -> Option<f32> {
137 self.to_number_value()
138 }
139}
140
141#[node]
142#[derive(
143 Parse, Peek, IntoCursor, ToSpan, SemanticEq, ToCursors, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
144)]
145#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
146#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
147#[derive(csskit_derives::NodeWithMetadata)]
148pub enum LengthPercentage {
149 #[cfg_attr(feature = "visitable", visit(skip))]
150 Zero(Exact<T![Number], 0>),
151 Length(Length),
152 #[cfg_attr(feature = "visitable", visit(skip))]
153 Percent(Percentage),
154}
155
156impl From<LengthPercentage> for f32 {
157 fn from(val: LengthPercentage) -> Self {
158 match val {
159 LengthPercentage::Zero(_) => 0.0,
160 LengthPercentage::Percent(f) => f.into(),
161 LengthPercentage::Length(f) => f.into(),
162 }
163 }
164}
165
166impl ToNumberValue for LengthPercentage {
167 fn to_number_value(&self) -> Option<f32> {
168 Some((*self).into())
169 }
170}
171
172impl ToNormalisedValue for LengthPercentage {
173 fn to_normalised_value(&self) -> Option<f32> {
174 self.to_number_value()
175 }
176}
177
178#[node]
179#[derive(
180 Parse, Peek, IntoCursor, ToSpan, SemanticEq, ToCursors, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
181)]
182#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
183#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
184#[derive(csskit_derives::NodeWithMetadata)]
185pub enum LengthPercentageNumber {
186 Length(Length),
187 #[cfg_attr(feature = "visitable", visit(skip))]
188 Percent(Percentage),
189 #[cfg_attr(feature = "visitable", visit(skip))]
190 Number(T![Number]),
191}
192
193impl From<LengthPercentageNumber> for f32 {
194 fn from(val: LengthPercentageNumber) -> Self {
195 match val {
196 LengthPercentageNumber::Length(f) => f.into(),
197 LengthPercentageNumber::Percent(f) => f.into(),
198 LengthPercentageNumber::Number(f) => f.into(),
199 }
200 }
201}
202
203impl ToNumberValue for LengthPercentageNumber {
204 fn to_number_value(&self) -> Option<f32> {
205 Some((*self).into())
206 }
207}
208
209#[node]
210#[derive(
211 Parse, Peek, IntoCursor, ToSpan, SemanticEq, ToCursors, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
212)]
213#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
214#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(children))]
215#[derive(csskit_derives::NodeWithMetadata)]
216pub enum LengthPercentageOrFlex {
217 Flex(Flex),
218 LengthPercentage(LengthPercentage),
219}
220
221impl From<LengthPercentageOrFlex> for f32 {
222 fn from(val: LengthPercentageOrFlex) -> Self {
223 match val {
224 LengthPercentageOrFlex::Flex(f) => f.into(),
225 LengthPercentageOrFlex::LengthPercentage(l) => l.into(),
226 }
227 }
228}
229
230impl ToNumberValue for LengthPercentageOrFlex {
231 fn to_number_value(&self) -> Option<f32> {
232 Some((*self).into())
233 }
234}
235
236impl ToNormalisedValue for LengthPercentageOrFlex {
237 fn to_normalised_value(&self) -> Option<f32> {
238 self.to_number_value()
239 }
240}
241
242#[node]
243#[derive(
244 Parse, Peek, ToCursors, IntoCursor, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
245)]
246#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
247#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
248#[derive(csskit_derives::NodeWithMetadata)]
249pub enum NumberLength {
250 #[cfg_attr(feature = "visitable", visit(skip))]
251 Number(T![Number]),
252 Length(Length),
253}
254
255impl From<NumberLength> for f32 {
256 fn from(val: NumberLength) -> Self {
257 match val {
258 NumberLength::Number(n) => n.into(),
259 NumberLength::Length(n) => n.into(),
260 }
261 }
262}
263
264impl ToNumberValue for NumberLength {
265 fn to_number_value(&self) -> Option<f32> {
266 Some((*self).into())
267 }
268}
269
270impl ToNormalisedValue for NumberLength {
271 fn to_normalised_value(&self) -> Option<f32> {
272 self.to_number_value()
273 }
274}
275
276#[cfg(test)]
277mod tests {
278 use super::*;
279 use crate::CssAtomSet;
280 use css_parse::assert_parse;
281
282 #[test]
283 fn test_writes() {
284 assert_parse!(CssAtomSet::ATOMS, Length, "10px");
285 assert_parse!(CssAtomSet::ATOMS, Length, "1.2345679px");
287 assert_parse!(CssAtomSet::ATOMS, Length, "-1px");
289 assert_parse!(CssAtomSet::ATOMS, LengthPercentage, "1%");
291 }
292}