raffia/parser/at_rule/
keyframes.rs

1use super::Parser;
2use crate::{
3    Parse,
4    ast::*,
5    eat,
6    error::{Error, ErrorKind, PResult},
7    parser::state::ParserState,
8    peek,
9    pos::{Span, Spanned},
10    tokenizer::Token,
11    util,
12};
13
14// https://drafts.csswg.org/css-animations/#keyframes
15impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for KeyframeBlock<'s> {
16    fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
17        let first_selector = input.parse::<KeyframeSelector>()?;
18        let start = first_selector.span().start;
19
20        let mut selectors = Vec::with_capacity(2);
21        selectors.push(first_selector);
22        let mut comma_spans = vec![];
23        while let Some((_, comma_span)) = eat!(input, Comma) {
24            comma_spans.push(comma_span);
25            selectors.push(input.parse()?);
26        }
27        debug_assert_eq!(comma_spans.len() + 1, selectors.len());
28
29        let block = input
30            .with_state(ParserState {
31                in_keyframes_at_rule: false,
32                ..input.state.clone()
33            })
34            .parse::<SimpleBlock>()?;
35
36        let span = Span {
37            start,
38            end: block.span.end,
39        };
40        Ok(KeyframeBlock {
41            selectors,
42            comma_spans,
43            block,
44            span,
45        })
46    }
47}
48
49impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for KeyframeSelector<'s> {
50    fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
51        match &peek!(input).token {
52            Token::Percentage(..) => Ok(KeyframeSelector::Percentage(input.parse()?)),
53            _ => {
54                let ident = input.parse()?;
55                match &ident {
56                    InterpolableIdent::Literal(ident)
57                        if !ident.name.eq_ignore_ascii_case("from")
58                            && !ident.name.eq_ignore_ascii_case("to") =>
59                    {
60                        input.recoverable_errors.push(Error {
61                            kind: ErrorKind::UnknownKeyframeSelectorIdent,
62                            span: ident.span.clone(),
63                        });
64                    }
65                    _ => {}
66                }
67                Ok(KeyframeSelector::Ident(ident))
68            }
69        }
70    }
71}
72
73// https://drafts.csswg.org/css-animations/#keyframes
74impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for KeyframesName<'s> {
75    fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
76        match &peek!(input).token {
77            Token::Ident(..) | Token::HashLBrace(..) | Token::AtLBraceVar(..) => {
78                let ident = input.parse()?;
79                match &ident {
80                    InterpolableIdent::Literal(ident)
81                        if util::is_css_wide_keyword(&ident.name)
82                            || ident.name.eq_ignore_ascii_case("default") =>
83                    {
84                        input.recoverable_errors.push(Error {
85                            kind: ErrorKind::CSSWideKeywordDisallowed,
86                            span: ident.span.clone(),
87                        });
88                    }
89                    _ => {}
90                }
91                Ok(KeyframesName::Ident(ident))
92            }
93            Token::AtKeyword(..) => input.parse().map(KeyframesName::LessVariable),
94            Token::Tilde(..) => input.parse().map(KeyframesName::LessEscapedStr),
95            _ => input.parse().map(KeyframesName::Str),
96        }
97    }
98}