Skip to main content

oxc_css_parser/parser/at_rule/
supports.rs

1use super::Parser;
2use crate::{
3    Parse,
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<'cmt, 's: 'cmt> Parse<'cmt, 's> for SupportsCondition<'s> {
13    fn parse(input: &mut Parser<'cmt, 's>) -> 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 {
19                    start: keyword.span.start,
20                    end: condition.span().end,
21                };
22                Ok(SupportsCondition {
23                    conditions: vec![SupportsConditionKind::Not(SupportsNot {
24                        keyword,
25                        condition,
26                        span: span.clone(),
27                    })],
28                    span,
29                })
30            }
31            _ => {
32                let first = input.parse::<SupportsInParens>()?;
33                let mut span = first.span().clone();
34                let mut conditions = vec![SupportsConditionKind::SupportsInParens(first)];
35                while let Token::Ident(ident) = &peek!(input).token {
36                    let name = ident.name();
37                    if name.eq_ignore_ascii_case("and") {
38                        let ident = input.parse::<Ident>()?;
39                        let condition = input.parse::<SupportsInParens>()?;
40                        let span = Span {
41                            start: ident.span.start,
42                            end: condition.span().end,
43                        };
44                        conditions.push(SupportsConditionKind::And(SupportsAnd {
45                            keyword: ident,
46                            condition,
47                            span,
48                        }));
49                    } else if name.eq_ignore_ascii_case("or") {
50                        let ident = input.parse::<Ident>()?;
51                        let condition = input.parse::<SupportsInParens>()?;
52                        let span = Span {
53                            start: ident.span.start,
54                            end: condition.span().end,
55                        };
56                        conditions.push(SupportsConditionKind::Or(SupportsOr {
57                            keyword: ident,
58                            condition,
59                            span,
60                        }));
61                    } else {
62                        break;
63                    }
64                }
65                if let Some(last) = conditions.last() {
66                    span.end = last.span().end;
67                }
68                Ok(SupportsCondition { conditions, span })
69            }
70        }
71    }
72}
73
74impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for SupportsInParens<'s> {
75    fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
76        match peek!(input) {
77            TokenWithSpan {
78                token: Token::LParen(..),
79                ..
80            } => input
81                .try_parse(|parser| {
82                    parser.parse::<SupportsDecl>().map(|supports_decl| {
83                        let span = supports_decl.span.clone();
84                        SupportsInParens {
85                            kind: SupportsInParensKind::Feature(Box::new(supports_decl)),
86                            span,
87                        }
88                    })
89                })
90                .or_else(|_| {
91                    let (_, Span { start, .. }) = expect!(input, LParen);
92                    let condition = input.parse()?;
93                    let (_, Span { end, .. }) = expect!(input, RParen);
94                    Ok(SupportsInParens {
95                        kind: SupportsInParensKind::SupportsCondition(condition),
96                        span: Span { start, end },
97                    })
98                }),
99            TokenWithSpan {
100                token: Token::Ident(..),
101                ..
102            } => {
103                let function_ident = input.parse::<Ident>()?;
104                if function_ident.name.eq_ignore_ascii_case("selector") {
105                    expect!(input, LParen);
106                    let selector_list = input.parse::<SelectorList>()?;
107                    expect!(input, RParen);
108                    let span = selector_list.span.clone();
109                    Ok(SupportsInParens {
110                        kind: SupportsInParensKind::Selector(selector_list),
111                        span,
112                    })
113                } else {
114                    let function =
115                        input.parse_function(InterpolableIdent::Literal(function_ident))?;
116                    let span = function.span.clone();
117                    Ok(SupportsInParens {
118                        kind: SupportsInParensKind::Function(function),
119                        span,
120                    })
121                }
122            }
123            TokenWithSpan { token, span } => Err(Error {
124                kind: ErrorKind::Unexpected("'('", token.symbol()),
125                span: span.clone(),
126            }),
127        }
128    }
129}
130
131impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for SupportsDecl<'s> {
132    fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
133        let start = expect!(input, LParen).1.start;
134        let decl = input.parse()?;
135        let end = expect!(input, RParen).1.end;
136        Ok(SupportsDecl {
137            decl,
138            span: Span { start, end },
139        })
140    }
141}