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		#[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		// Truncates to 7dp
286		assert_parse!(CssAtomSet::ATOMS, Length, "1.2345679px");
287		// Removes redundant dp
288		assert_parse!(CssAtomSet::ATOMS, Length, "-1px");
289		// Percent
290		assert_parse!(CssAtomSet::ATOMS, LengthPercentage, "1%");
291	}
292}