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