Skip to main content

css_ast/units/
angles.rs

1use super::prelude::*;
2
3/// <https://drafts.csswg.org/css-values/#angles>
4///
5/// ```text,ignore
6/// <angle> = <dimension-token>
7/// ```
8#[derive(
9	IntoCursor, ToSpan, SemanticEq, Parse, Peek, ToCursors, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
10)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
12#[cfg_attr(feature = "css_feature_data", derive(::csskit_derives::ToCSSFeature), css_feature("css.types.angle"))]
13#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
14#[derive(csskit_derives::NodeWithMetadata)]
15#[metadata(node_kinds = Dimension)]
16pub enum Angle {
17	#[atom(CssAtomSet::Grad)]
18	Grad(T![Dimension]),
19	#[atom(CssAtomSet::Rad)]
20	Rad(T![Dimension]),
21	#[atom(CssAtomSet::Turn)]
22	Turn(T![Dimension]),
23	#[atom(CssAtomSet::Deg)]
24	Deg(T![Dimension]),
25}
26
27impl From<Angle> for f32 {
28	fn from(val: Angle) -> Self {
29		match val {
30			Angle::Grad(f) => f.into(),
31			Angle::Rad(f) => f.into(),
32			Angle::Turn(f) => f.into(),
33			Angle::Deg(f) => f.into(),
34		}
35	}
36}
37
38impl ToNumberValue for Angle {
39	fn to_number_value(&self) -> Option<f32> {
40		Some((*self).into())
41	}
42}
43
44impl Angle {
45	const DEG_GRAD: f32 = 0.9;
46	const DEG_RAD: f32 = 57.295_78;
47	const DEG_TURN: f32 = 360.0;
48
49	pub fn as_degrees(&self) -> f32 {
50		match self {
51			Self::Grad(d) => Into::<f32>::into(*d) * Self::DEG_GRAD,
52			Self::Rad(d) => Into::<f32>::into(*d) * Self::DEG_RAD,
53			Self::Turn(d) => Into::<f32>::into(*d) * Self::DEG_TURN,
54			Self::Deg(d) => (*d).into(),
55		}
56	}
57}
58
59#[derive(
60	IntoCursor, ToSpan, SemanticEq, Parse, Peek, ToCursors, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
61)]
62#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
63#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(children))]
64#[derive(csskit_derives::NodeWithMetadata)]
65pub enum AngleOrZero {
66	Angle(Angle),
67	#[cfg_attr(feature = "visitable", visit(skip))]
68	Zero(Exact<T![Number], 0>),
69}
70
71#[derive(
72	IntoCursor, ToSpan, SemanticEq, Parse, Peek, ToCursors, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
73)]
74#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
75#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(children))]
76#[derive(csskit_derives::NodeWithMetadata)]
77pub enum AngleOrNumber {
78	Angle(Angle),
79	#[cfg_attr(feature = "visitable", visit(skip))]
80	Number(T![Number]),
81}
82
83impl From<AngleOrZero> for f32 {
84	fn from(val: AngleOrZero) -> Self {
85		match val {
86			AngleOrZero::Angle(f) => f.into(),
87			AngleOrZero::Zero(f) => f.0.into(),
88		}
89	}
90}
91
92#[cfg(test)]
93mod tests {
94	use super::*;
95	use crate::CssAtomSet;
96	use css_parse::assert_parse;
97
98	#[test]
99	fn size_test() {
100		assert_eq!(std::mem::size_of::<Angle>(), 16);
101	}
102
103	#[test]
104	fn test_writes() {
105		assert_parse!(CssAtomSet::ATOMS, Angle, "0grad");
106		assert_parse!(CssAtomSet::ATOMS, Angle, "0deg");
107		assert_parse!(CssAtomSet::ATOMS, AngleOrZero, "0", AngleOrZero::Zero(_));
108	}
109}