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, 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(Parse, Peek, IntoCursor, ToCursors, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
135#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
136#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
137#[derive(csskit_derives::NodeWithMetadata)]
138pub enum LengthPercentage {
139 #[cfg_attr(feature = "visitable", visit(skip))]
140 Zero(Exact<T![Number], 0>),
141 Length(Length),
142 #[cfg_attr(feature = "visitable", visit(skip))]
143 Percent(Percentage),
144}
145
146impl From<LengthPercentage> for f32 {
147 fn from(val: LengthPercentage) -> Self {
148 match val {
149 LengthPercentage::Zero(_) => 0.0,
150 LengthPercentage::Percent(f) => f.into(),
151 LengthPercentage::Length(f) => f.into(),
152 }
153 }
154}
155
156impl ToNumberValue for LengthPercentage {
157 fn to_number_value(&self) -> Option<f32> {
158 Some((*self).into())
159 }
160}
161
162#[derive(Parse, Peek, IntoCursor, ToCursors, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
163#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
164#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(children))]
165#[derive(csskit_derives::NodeWithMetadata)]
166pub enum LengthPercentageOrFlex {
167 Flex(Flex),
168 LengthPercentage(LengthPercentage),
169}
170
171impl From<LengthPercentageOrFlex> for f32 {
172 fn from(val: LengthPercentageOrFlex) -> Self {
173 match val {
174 LengthPercentageOrFlex::Flex(f) => f.into(),
175 LengthPercentageOrFlex::LengthPercentage(l) => l.into(),
176 }
177 }
178}
179
180impl ToNumberValue for LengthPercentageOrFlex {
181 fn to_number_value(&self) -> Option<f32> {
182 Some((*self).into())
183 }
184}
185
186#[derive(Parse, Peek, ToCursors, IntoCursor, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
187#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
188#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
189#[derive(csskit_derives::NodeWithMetadata)]
190pub enum NumberLength {
191 #[cfg_attr(feature = "visitable", visit(skip))]
192 Number(T![Number]),
193 Length(Length),
194}
195
196impl From<NumberLength> for f32 {
197 fn from(val: NumberLength) -> Self {
198 match val {
199 NumberLength::Number(n) => n.into(),
200 NumberLength::Length(n) => n.into(),
201 }
202 }
203}
204
205impl ToNumberValue for NumberLength {
206 fn to_number_value(&self) -> Option<f32> {
207 Some((*self).into())
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use super::*;
214 use crate::CssAtomSet;
215 use css_parse::assert_parse;
216
217 #[test]
218 fn size_test() {
219 assert_eq!(std::mem::size_of::<Length>(), 16);
220 assert_eq!(std::mem::size_of::<LengthPercentage>(), 16);
221 assert_eq!(std::mem::size_of::<NumberLength>(), 16);
222 }
223
224 #[test]
225 fn test_writes() {
226 assert_parse!(CssAtomSet::ATOMS, Length, "10px");
227 assert_parse!(CssAtomSet::ATOMS, Length, "1.2345679px");
229 assert_parse!(CssAtomSet::ATOMS, Length, "-1px");
231 assert_parse!(CssAtomSet::ATOMS, LengthPercentage, "1%");
233 }
234}