oxc_css_parser/parser/at_rule/
container.rs1use 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 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
246impl<'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}