raffia/parser/at_rule/
keyframes.rs1use 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
14impl<'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
73impl<'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}