Skip to main content

oxc_css_parser/parser/at_rule/
container.rs

1use super::Parser;
2use crate::{
3    Parse, arena_box, arena_vec,
4    ast::*,
5    eat,
6    error::{Error, ErrorKind, PResult},
7    expect, expect_without_ws_or_comments, peek,
8    pos::{Span, Spanned},
9    tokenizer::{Token, TokenWithSpan},
10};
11
12impl<'a> Parse<'a> for ContainerCondition<'a> {
13    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
14        match &peek!(input).token {
15            Token::Ident(ident) if ident.name().eq_ignore_ascii_case("not") => {
16                let container_condition_not = input.parse::<ContainerConditionNot>()?;
17                let span = container_condition_not.span.clone();
18                Ok(ContainerCondition {
19                    conditions: arena_vec!(input; ContainerConditionKind::Not(container_condition_not)),
20                    span,
21                })
22            }
23            _ => {
24                let first = input.parse::<QueryInParens>()?;
25                let mut span = first.span.clone();
26                let mut conditions =
27                    arena_vec!(input; ContainerConditionKind::QueryInParens(first));
28                if let Token::Ident(ident) = &peek!(input).token {
29                    let name = ident.name();
30                    if name.eq_ignore_ascii_case("and") {
31                        loop {
32                            conditions.push(ContainerConditionKind::And(input.parse()?));
33                            match &peek!(input).token {
34                                Token::Ident(ident) if ident.name().eq_ignore_ascii_case("and") => {
35                                }
36                                _ => break,
37                            }
38                        }
39                    } else if name.eq_ignore_ascii_case("or") {
40                        loop {
41                            conditions.push(ContainerConditionKind::Or(input.parse()?));
42                            match &peek!(input).token {
43                                Token::Ident(ident) if ident.name().eq_ignore_ascii_case("or") => {}
44                                _ => break,
45                            }
46                        }
47                    }
48                }
49
50                if let Some(last) = conditions.last() {
51                    span.end = last.span().end;
52                }
53                Ok(ContainerCondition { conditions, span })
54            }
55        }
56    }
57}
58
59impl<'a> Parse<'a> for ContainerConditionAnd<'a> {
60    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
61        let keyword = input.parse::<Ident>()?;
62        if keyword.name.eq_ignore_ascii_case("and") {
63            let query_in_parens = input.parse::<QueryInParens>()?;
64            let span = Span { start: keyword.span.start, end: query_in_parens.span.end };
65            Ok(ContainerConditionAnd { keyword, query_in_parens, span })
66        } else {
67            Err(Error { kind: ErrorKind::ExpectContainerConditionAnd, span: keyword.span })
68        }
69    }
70}
71
72impl<'a> Parse<'a> for ContainerConditionNot<'a> {
73    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
74        let keyword = input.parse::<Ident>()?;
75        if keyword.name.eq_ignore_ascii_case("not") {
76            let query_in_parens = input.parse::<QueryInParens>()?;
77            let span = Span { start: keyword.span.start, end: query_in_parens.span.end };
78            Ok(ContainerConditionNot { keyword, query_in_parens, span })
79        } else {
80            Err(Error { kind: ErrorKind::ExpectContainerConditionNot, span: keyword.span })
81        }
82    }
83}
84
85impl<'a> Parse<'a> for ContainerConditionOr<'a> {
86    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
87        let keyword = input.parse::<Ident>()?;
88        if keyword.name.eq_ignore_ascii_case("or") {
89            let query_in_parens = input.parse::<QueryInParens>()?;
90            let span = Span { start: keyword.span.start, end: query_in_parens.span.end };
91            Ok(ContainerConditionOr { keyword, query_in_parens, span })
92        } else {
93            Err(Error { kind: ErrorKind::ExpectContainerConditionOr, span: keyword.span })
94        }
95    }
96}
97
98impl<'a> Parse<'a> for QueryInParens<'a> {
99    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
100        if let Some((_, Span { start, .. })) = eat!(input, LParen) {
101            let kind = if let Ok(container_condition) = input.try_parse(ContainerCondition::parse) {
102                QueryInParensKind::ContainerCondition(container_condition)
103            } else {
104                QueryInParensKind::SizeFeature(arena_box!(input, input.parse()?))
105            };
106            let (_, Span { end, .. }) = expect!(input, RParen);
107            Ok(QueryInParens { kind, span: Span { start, end } })
108        } else {
109            let (style_keyword, ident_span) = expect!(input, Ident);
110            let keyword = style_keyword.name();
111            if keyword.eq_ignore_ascii_case("style") {
112                expect_without_ws_or_comments!(input, LParen);
113                let kind = input.parse().map(QueryInParensKind::StyleQuery)?;
114                let (_, Span { end, .. }) = expect!(input, RParen);
115                Ok(QueryInParens { kind, span: Span { start: ident_span.start, end } })
116            } else if keyword.eq_ignore_ascii_case("scroll-state") {
117                // https://drafts.csswg.org/css-conditional-5/#scroll-state-container
118                expect_without_ws_or_comments!(input, LParen);
119                let media = input.parse()?;
120                let kind = QueryInParensKind::ScrollState(arena_box!(input, media));
121                let (_, Span { end, .. }) = expect!(input, RParen);
122                Ok(QueryInParens { kind, span: Span { start: ident_span.start, end } })
123            } else {
124                Err(Error { kind: ErrorKind::ExpectStyleQuery, span: ident_span })
125            }
126        }
127    }
128}
129
130impl<'a> Parse<'a> for StyleCondition<'a> {
131    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
132        match &peek!(input).token {
133            Token::Ident(ident) if ident.name().eq_ignore_ascii_case("not") => {
134                let style_condition_not = input.parse::<StyleConditionNot>()?;
135                let span = style_condition_not.span.clone();
136                Ok(StyleCondition {
137                    conditions: arena_vec!(input; StyleConditionKind::Not(style_condition_not)),
138                    span,
139                })
140            }
141            _ => {
142                let first = input.parse::<StyleInParens>()?;
143                let mut span = first.span.clone();
144                let mut conditions = arena_vec!(input; StyleConditionKind::StyleInParens(first));
145                if let Token::Ident(ident) = &peek!(input).token {
146                    let name = ident.name();
147                    if name.eq_ignore_ascii_case("and") {
148                        loop {
149                            conditions.push(StyleConditionKind::And(input.parse()?));
150                            match &peek!(input).token {
151                                Token::Ident(ident) if ident.name().eq_ignore_ascii_case("and") => {
152                                }
153                                _ => break,
154                            }
155                        }
156                    } else if name.eq_ignore_ascii_case("or") {
157                        loop {
158                            conditions.push(StyleConditionKind::Or(input.parse()?));
159                            match &peek!(input).token {
160                                Token::Ident(ident) if ident.name().eq_ignore_ascii_case("or") => {}
161                                _ => break,
162                            }
163                        }
164                    }
165                }
166
167                if let Some(last) = conditions.last() {
168                    span.end = last.span().end;
169                }
170                Ok(StyleCondition { conditions, span })
171            }
172        }
173    }
174}
175
176impl<'a> Parse<'a> for StyleConditionAnd<'a> {
177    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
178        let ident = input.parse::<Ident>()?;
179        if ident.name.eq_ignore_ascii_case("and") {
180            let style_in_parens = input.parse::<StyleInParens>()?;
181            let span = Span { start: ident.span.start, end: style_in_parens.span.end };
182            Ok(StyleConditionAnd { keyword: ident, style_in_parens, span })
183        } else {
184            Err(Error { kind: ErrorKind::ExpectStyleConditionAnd, span: ident.span })
185        }
186    }
187}
188
189impl<'a> Parse<'a> for StyleConditionNot<'a> {
190    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
191        let keyword = input.parse::<Ident>()?;
192        if keyword.name.eq_ignore_ascii_case("not") {
193            let style_in_parens = input.parse::<StyleInParens>()?;
194            let span = Span { start: keyword.span.start, end: style_in_parens.span.end };
195            Ok(StyleConditionNot { keyword, style_in_parens, span })
196        } else {
197            Err(Error { kind: ErrorKind::ExpectStyleConditionNot, span: keyword.span })
198        }
199    }
200}
201
202impl<'a> Parse<'a> for StyleConditionOr<'a> {
203    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
204        let keyword = input.parse::<Ident>()?;
205        if keyword.name.eq_ignore_ascii_case("or") {
206            let style_in_parens = input.parse::<StyleInParens>()?;
207            let span = Span { start: keyword.span.start, end: style_in_parens.span.end };
208            Ok(StyleConditionOr { keyword, style_in_parens, span })
209        } else {
210            Err(Error { kind: ErrorKind::ExpectStyleConditionOr, span: keyword.span })
211        }
212    }
213}
214
215impl<'a> Parse<'a> for StyleInParens<'a> {
216    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
217        let (_, Span { start, .. }) = expect!(input, LParen);
218        let kind = input.parse()?;
219        let (_, Span { end, .. }) = expect!(input, RParen);
220        Ok(StyleInParens { kind, span: Span { start, end } })
221    }
222}
223
224impl<'a> Parse<'a> for StyleInParensKind<'a> {
225    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
226        if let Ok(style_condition) = input.try_parse(StyleCondition::parse) {
227            Ok(StyleInParensKind::Condition(style_condition))
228        } else {
229            input.parse().map(StyleInParensKind::Feature)
230        }
231    }
232}
233
234impl<'a> Parse<'a> for StyleQuery<'a> {
235    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
236        if let Ok(condition) = input.try_parse(StyleCondition::parse) {
237            Ok(StyleQuery::Condition(condition))
238        } else {
239            let feature = input.parse().map(StyleQuery::Feature);
240            eat!(input, Semicolon);
241            feature
242        }
243    }
244}
245
246// https://drafts.csswg.org/css-contain-3/#container-rule
247impl<'a> Parse<'a> for ContainerPrelude<'a> {
248    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
249        let name = input.try_parse(|parser| match parser.parse()? {
250            InterpolableIdent::Literal(ident)
251                if ident.name.eq_ignore_ascii_case("not")
252                    || ident.name.eq_ignore_ascii_case("scroll-state") =>
253            {
254                Err(Error { kind: ErrorKind::TryParseError, span: ident.span })
255            }
256            InterpolableIdent::Literal(ident) if ident.name.eq_ignore_ascii_case("style") => {
257                match peek!(parser) {
258                    TokenWithSpan { token: Token::LParen(..), span }
259                        if span.start == ident.span.end =>
260                    {
261                        Err(Error { kind: ErrorKind::TryParseError, span: ident.span })
262                    }
263                    _ => Ok(InterpolableIdent::Literal(ident)),
264                }
265            }
266            ident => Ok(ident),
267        });
268        let condition = input.parse::<ContainerCondition>()?;
269        let mut span = condition.span().clone();
270        if let Ok(name) = &name {
271            span.start = name.span().start;
272        }
273        Ok(ContainerPrelude { name: name.ok(), condition, span })
274    }
275}