1use super::prelude::*;
2use crate::Percentage;
3use css_parse::NoBlockAllowed;
4
5#[derive(Peek, Parse, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
8#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
9#[cfg_attr(feature = "css_feature_data", derive(::csskit_derives::ToCSSFeature), css_feature("css.at-rules.keyframes"))]
10pub struct KeyframesRule<'a> {
11 #[cfg_attr(feature = "visitable", visit(skip))]
12 #[atom(CssAtomSet::Keyframes)]
13 pub name: T![AtKeyword],
14 pub prelude: KeyframesName,
15 pub block: KeyframesRuleBlock<'a>,
16}
17
18impl<'a> NodeWithMetadata<CssMetadata> for KeyframesRule<'a> {
19 fn metadata(&self) -> CssMetadata {
20 let mut meta = self.block.0.metadata();
21 meta.used_at_rules |= AtRuleId::Keyframes;
22 meta
23 }
24}
25
26#[derive(Peek, ToCursors, IntoCursor, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
27#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
28#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
29pub enum KeyframesName {
30 Ident(T![Ident]),
31 String(T![String]),
32}
33
34impl KeyframesName {
35 fn invalid_ident(atom: CssAtomSet) -> bool {
36 matches!(atom, CssAtomSet::Default | CssAtomSet::Initial | CssAtomSet::Unset | CssAtomSet::None)
37 }
38}
39
40impl<'a> Parse<'a> for KeyframesName {
42 fn parse<I>(p: &mut Parser<'a, I>) -> ParserResult<Self>
43 where
44 I: Iterator<Item = Cursor> + Clone,
45 {
46 if p.peek::<T![String]>() {
47 return Ok(Self::String(p.parse::<T![String]>()?));
48 }
49 let ident = p.parse::<T![Ident]>()?;
50 if KeyframesName::invalid_ident(p.to_atom::<CssAtomSet>(ident.into())) {
51 Err(Diagnostic::new(ident.into(), Diagnostic::reserved_keyframe_name))?
52 }
53 Ok(Self::Ident(ident))
54 }
55}
56
57#[derive(Parse, Peek, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
58#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
59#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
60pub struct KeyframesRuleBlock<'a>(pub RuleList<'a, Keyframe<'a>, CssMetadata>);
61
62#[derive(Parse, Peek, ToSpan, ToCursors, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
63#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
64#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
65pub struct Keyframe<'a>(QualifiedRule<'a, KeyframeSelectors<'a>, StyleValue<'a>, NoBlockAllowed, CssMetadata>);
66
67impl<'a> NodeWithMetadata<CssMetadata> for Keyframe<'a> {
68 fn metadata(&self) -> CssMetadata {
69 self.0.metadata()
70 }
71}
72
73#[derive(Peek, Parse, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
74#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
75#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(children))]
76pub struct KeyframeSelectors<'a>(pub CommaSeparated<'a, KeyframeSelector>);
77
78#[derive(Peek, Parse, ToCursors, IntoCursor, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
79#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
80#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
81pub enum KeyframeSelector {
82 #[atom(CssAtomSet::From)]
83 From(T![Ident]),
84 #[atom(CssAtomSet::To)]
85 To(T![Ident]),
86 Percent(Percentage),
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92 use crate::CssAtomSet;
93 use css_parse::assert_parse;
94
95 #[test]
96 fn size_test() {
97 assert_eq!(std::mem::size_of::<KeyframesRule>(), 120);
98 assert_eq!(std::mem::size_of::<KeyframeSelector>(), 16);
99 assert_eq!(std::mem::size_of::<KeyframesName>(), 16);
100 assert_eq!(std::mem::size_of::<KeyframesRuleBlock>(), 88);
101 }
102
103 #[test]
104 fn test_writes() {
105 assert_parse!(CssAtomSet::ATOMS, KeyframesRule, "@keyframes foo{}");
106 assert_parse!(CssAtomSet::ATOMS, KeyframesRule, "@keyframes\"include\"{}");
107 assert_parse!(CssAtomSet::ATOMS, KeyframesRule, "@keyframes spin{0%{rotate:0deg}100%{rotate:360deg}}");
108 assert_parse!(CssAtomSet::ATOMS, KeyframesRule, "@keyframes spin{from,0%{rotate:0deg}to,100%{rotate:360deg}}");
109 assert_parse!(CssAtomSet::ATOMS, KeyframesRule, "@keyframes spin{to{rotate:360deg}}");
110 assert_parse!(
111 CssAtomSet::ATOMS,
112 KeyframesRule,
113 "@keyframes x{to{animation-timing-function:cubic-bezier(0,0,0.2,1)}}"
114 );
115 }
116}