Skip to main content

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, 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		// Truncates to 7dp
264		assert_parse!(CssAtomSet::ATOMS, Length, "1.2345679px");
265		// Removes redundant dp
266		assert_parse!(CssAtomSet::ATOMS, Length, "-1px");
267		// Percent
268		assert_parse!(CssAtomSet::ATOMS, LengthPercentage, "1%");
269	}
270}