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
92/// <https://drafts.csswg.org/css-values/#typedef-angle-percentage>
93///
94/// ```text,ignore
95/// <angle-percentage> = [ <angle> | <percentage> ]
96/// ```
97///
98/// Also admits `<zero>`, as every use in the gradient grammars does.
99#[node]
100#[derive(
101	IntoCursor, ToSpan, SemanticEq, Parse, Peek, ToCursors, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
102)]
103#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
104#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(children))]
105#[derive(csskit_derives::NodeWithMetadata)]
106pub enum AnglePercentage {
107	#[cfg_attr(feature = "visitable", visit(skip))]
108	Zero(Exact<T![Number], 0>),
109	Angle(Angle),
110	#[cfg_attr(feature = "visitable", visit(skip))]
111	Percent(crate::Percentage),
112}
113
114impl From<AnglePercentage> for f32 {
115	fn from(val: AnglePercentage) -> Self {
116		match val {
117			AnglePercentage::Zero(_) => 0.0,
118			AnglePercentage::Angle(f) => f.into(),
119			AnglePercentage::Percent(f) => f.into(),
120		}
121	}
122}
123
124impl ToNumberValue for AnglePercentage {
125	fn to_number_value(&self) -> Option<f32> {
126		Some((*self).into())
127	}
128}
129
130impl From<AngleOrZero> for f32 {
131	fn from(val: AngleOrZero) -> Self {
132		match val {
133			AngleOrZero::Angle(f) => f.into(),
134			AngleOrZero::Zero(f) => f.0.into(),
135		}
136	}
137}
138
139#[cfg(test)]
140mod tests {
141	use super::*;
142	use crate::CssAtomSet;
143	use css_parse::assert_parse;
144
145	#[test]
146	fn test_writes() {
147		assert_parse!(CssAtomSet::ATOMS, Angle, "0grad");
148		assert_parse!(CssAtomSet::ATOMS, Angle, "0deg");
149		assert_parse!(CssAtomSet::ATOMS, AngleOrZero, "0", AngleOrZero::Zero(_));
150	}
151}