css_ast/units/
length.rs

1use super::prelude::*;
2use crate::{Exact, 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		#[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		// Truncates to 7dp
228		assert_parse!(CssAtomSet::ATOMS, Length, "1.2345679px");
229		// Removes redundant dp
230		assert_parse!(CssAtomSet::ATOMS, Length, "-1px");
231		// Percent
232		assert_parse!(CssAtomSet::ATOMS, LengthPercentage, "1%");
233	}
234}