css_ast/units/
length.rs

1use super::prelude::*;
2use crate::{Flex, Percentage};
3
4macro_rules! apply_lengths {
5	($ident: ident) => {
6		$ident! {
7			// https://drafts.csswg.org/css-values/#font-relative-lengths
8			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			// https://drafts.csswg.org/css-values/#viewport-relative-units
22			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			// https://drafts.csswg.org/css-values/#absolute-lengths
48			Cm,
49			Mm,
50			Q,
51			In,
52			Pc,
53			Pt,
54			Px,
55
56			// https://www.w3.org/TR/css-contain-3/#container-lengths
57			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		pub enum Length {
73			Zero(#[in_range(0.0..0.0)] T![Number]),
74			$(
75				#[atom(CssAtomSet::$name)]
76				$name(T![Dimension]),
77			)+
78		}
79	}
80}
81apply_lengths!(define_length);
82
83impl Length {
84	const PX_CM: f32 = Self::PX_IN / 2.54;
85	const PX_MM: f32 = Self::PX_IN / 25.4;
86	const PX_Q: f32 = Self::PX_MM / 4.0;
87	const PX_IN: f32 = 96.0;
88	const PX_PC: f32 = Self::PX_IN / 6.0;
89	const PX_PT: f32 = Self::PX_IN / 72.0;
90
91	pub fn to_px(&self) -> Option<f32> {
92		match self {
93			Self::Zero(_) => Some(0.0),
94			Self::Cm(d) => Some(Into::<f32>::into(*d) * Self::PX_CM),
95			Self::Mm(d) => Some(Into::<f32>::into(*d) * Self::PX_MM),
96			Self::Q(d) => Some(Into::<f32>::into(*d) * Self::PX_Q),
97			Self::In(d) => Some(Into::<f32>::into(*d) * Self::PX_IN),
98			Self::Pc(d) => Some(Into::<f32>::into(*d) * Self::PX_PC),
99			Self::Pt(d) => Some(Into::<f32>::into(*d) * Self::PX_PT),
100			_ => None,
101		}
102	}
103}
104
105impl From<Length> for f32 {
106	fn from(val: Length) -> Self {
107		macro_rules! match_length {
108			( $($name: ident),+ $(,)* ) => {
109				match val {
110					Length::Zero(_) => 0.0,
111					$(Length::$name(f) => f.into()),+
112				}
113			}
114		}
115		apply_lengths!(match_length)
116	}
117}
118
119impl PartialEq<f32> for Length {
120	fn eq(&self, other: &f32) -> bool {
121		let f: f32 = (*self).into();
122		f == *other
123	}
124}
125
126impl ToNumberValue for Length {
127	fn to_number_value(&self) -> Option<f32> {
128		Some((*self).into())
129	}
130}
131
132#[derive(Parse, Peek, IntoCursor, ToCursors, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
133#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
134#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
135pub enum LengthPercentage {
136	#[cfg_attr(feature = "visitable", visit(skip))]
137	Zero(#[in_range(0.0..0.0)] T![Number]),
138	Length(Length),
139	#[cfg_attr(feature = "visitable", visit(skip))]
140	Percent(Percentage),
141}
142
143impl From<LengthPercentage> for f32 {
144	fn from(val: LengthPercentage) -> Self {
145		match val {
146			LengthPercentage::Zero(_) => 0.0,
147			LengthPercentage::Percent(f) => f.into(),
148			LengthPercentage::Length(f) => f.into(),
149		}
150	}
151}
152
153impl ToNumberValue for LengthPercentage {
154	fn to_number_value(&self) -> Option<f32> {
155		Some((*self).into())
156	}
157}
158
159#[derive(Parse, Peek, IntoCursor, ToCursors, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
160#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
161#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(children))]
162pub enum LengthPercentageOrFlex {
163	Flex(Flex),
164	LengthPercentage(LengthPercentage),
165}
166
167#[derive(Parse, Peek, ToCursors, IntoCursor, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
168#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
169#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
170pub enum NumberLength {
171	#[cfg_attr(feature = "visitable", visit(skip))]
172	Number(T![Number]),
173	Length(Length),
174}
175
176impl From<NumberLength> for f32 {
177	fn from(val: NumberLength) -> Self {
178		match val {
179			NumberLength::Number(n) => n.into(),
180			NumberLength::Length(n) => n.into(),
181		}
182	}
183}
184
185impl ToNumberValue for NumberLength {
186	fn to_number_value(&self) -> Option<f32> {
187		Some((*self).into())
188	}
189}
190
191#[cfg(test)]
192mod tests {
193	use super::*;
194	use crate::CssAtomSet;
195	use css_parse::assert_parse;
196
197	#[test]
198	fn size_test() {
199		assert_eq!(std::mem::size_of::<Length>(), 16);
200		assert_eq!(std::mem::size_of::<LengthPercentage>(), 16);
201		assert_eq!(std::mem::size_of::<NumberLength>(), 16);
202	}
203
204	#[test]
205	fn test_writes() {
206		assert_parse!(CssAtomSet::ATOMS, Length, "10px");
207		// Truncates to 7dp
208		assert_parse!(CssAtomSet::ATOMS, Length, "1.2345679px");
209		// Removes redundant dp
210		assert_parse!(CssAtomSet::ATOMS, Length, "-1px");
211		// Percent
212		assert_parse!(CssAtomSet::ATOMS, LengthPercentage, "1%");
213	}
214}