Skip to main content

oxc_css_parser/parser/at_rule/
supports.rs

1use super::Parser;
2use crate::{
3    Parse, arena_box, arena_vec,
4    ast::*,
5    error::{Error, ErrorKind, PResult},
6    expect, peek,
7    pos::{Span, Spanned},
8    tokenizer::{Token, TokenWithSpan},
9};
10
11// https://drafts.csswg.org/css-conditional-3/#at-supports
12impl<'a> Parse<'a> for SupportsCondition<'a> {
13    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
14        match &peek!(input).token {
15            Token::Ident(token) if token.name().eq_ignore_ascii_case("not") => {
16                let keyword = input.parse::<Ident>()?;
17                let condition = input.parse::<SupportsInParens>()?;
18                let span = Span { start: keyword.span.start, end: condition.span().end };
19                Ok(SupportsCondition {
20                    conditions: arena_vec!(input; SupportsConditionKind::Not(SupportsNot {
21                        keyword,
22                        condition,
23                        span: span.clone(),
24                    })),
25                    span,
26                })
27            }
28            _ => {
29                let first = input.parse::<SupportsInParens>()?;
30                let mut span = first.span().clone();
31                let mut conditions =
32                    arena_vec!(input; SupportsConditionKind::SupportsInParens(first));
33                while let Token::Ident(ident) = &peek!(input).token {
34                    let name = ident.name();
35                    if name.eq_ignore_ascii_case("and") {
36                        let ident = input.parse::<Ident>()?;
37                        let condition = input.parse::<SupportsInParens>()?;
38                        let span = Span { start: ident.span.start, end: condition.span().end };
39                        conditions.push(SupportsConditionKind::And(SupportsAnd {
40                            keyword: ident,
41                            condition,
42                            span,
43                        }));
44                    } else if name.eq_ignore_ascii_case("or") {
45                        let ident = input.parse::<Ident>()?;
46                        let condition = input.parse::<SupportsInParens>()?;
47                        let span = Span { start: ident.span.start, end: condition.span().end };
48                        conditions.push(SupportsConditionKind::Or(SupportsOr {
49                            keyword: ident,
50                            condition,
51                            span,
52                        }));
53                    } else {
54                        break;
55                    }
56                }
57                if let Some(last) = conditions.last() {
58                    span.end = last.span().end;
59                }
60                Ok(SupportsCondition { conditions, span })
61            }
62        }
63    }
64}
65
66impl<'a> Parse<'a> for SupportsInParens<'a> {
67    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
68        match peek!(input) {
69            TokenWithSpan { token: Token::LParen(..), .. } => input
70                .try_parse(|parser| {
71                    parser.parse::<SupportsDecl>().map(|supports_decl| {
72                        let span = supports_decl.span.clone();
73                        SupportsInParens {
74                            kind: SupportsInParensKind::Feature(arena_box!(parser, supports_decl)),
75                            span,
76                        }
77                    })
78                })
79                .or_else(|_| {
80                    input.try_parse(|parser| {
81                        let (_, Span { start, .. }) = expect!(parser, LParen);
82                        let condition = parser.parse::<SupportsCondition>()?;
83                        let (_, Span { end, .. }) = expect!(parser, RParen);
84                        Ok(SupportsInParens {
85                            kind: SupportsInParensKind::SupportsCondition(condition),
86                            span: Span { start, end },
87                        })
88                    })
89                })
90                .or_else(|_| {
91                    // <general-enclosed>: MQ L4 catch-all (referenced from <supports-condition>),
92                    // evaluates false at runtime.
93                    let (_, Span { start, .. }) = expect!(input, LParen);
94                    let tokens = input.parse_tokens_in_parens()?;
95                    let (_, Span { end, .. }) = expect!(input, RParen);
96                    Ok(SupportsInParens {
97                        kind: SupportsInParensKind::GeneralEnclosed(tokens),
98                        span: Span { start, end },
99                    })
100                }),
101            TokenWithSpan { token: Token::Ident(..), .. } => {
102                let function_ident = input.parse::<Ident>()?;
103                if function_ident.name.eq_ignore_ascii_case("selector") {
104                    expect!(input, LParen);
105                    let selector_list = input.parse::<SelectorList>()?;
106                    expect!(input, RParen);
107                    let span = selector_list.span.clone();
108                    Ok(SupportsInParens {
109                        kind: SupportsInParensKind::Selector(selector_list),
110                        span,
111                    })
112                } else {
113                    let function =
114                        input.parse_function(InterpolableIdent::Literal(function_ident))?;
115                    let span = function.span.clone();
116                    Ok(SupportsInParens { kind: SupportsInParensKind::Function(function), span })
117                }
118            }
119            TokenWithSpan { token, span } => Err(Error {
120                kind: ErrorKind::Unexpected("'('", token.symbol()),
121                span: span.clone(),
122            }),
123        }
124    }
125}
126
127impl<'a> Parse<'a> for SupportsDecl<'a> {
128    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
129        let start = expect!(input, LParen).1.start;
130        let decl = input.parse()?;
131        let end = expect!(input, RParen).1.end;
132        Ok(SupportsDecl { decl, span: Span { start, end } })
133    }
134}