oxc_css_parser/parser/at_rule/
container.rs1use super::Parser;
2use crate::{
3 Parse,
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<'cmt, 's: 'cmt> Parse<'cmt, 's> for ContainerCondition<'s> {
13 fn parse(input: &mut Parser<'cmt, 's>) -> 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: vec![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 = vec![ContainerConditionKind::QueryInParens(first)];
27 if let Token::Ident(ident) = &peek!(input).token {
28 let name = ident.name();
29 if name.eq_ignore_ascii_case("and") {
30 loop {
31 conditions.push(ContainerConditionKind::And(input.parse()?));
32 match &peek!(input).token {
33 Token::Ident(ident) if ident.name().eq_ignore_ascii_case("and") => {
34 }
35 _ => break,
36 }
37 }
38 } else if name.eq_ignore_ascii_case("or") {
39 loop {
40 conditions.push(ContainerConditionKind::Or(input.parse()?));
41 match &peek!(input).token {
42 Token::Ident(ident) if ident.name().eq_ignore_ascii_case("or") => {}
43 _ => break,
44 }
45 }
46 }
47 }
48
49 if let Some(last) = conditions.last() {
50 span.end = last.span().end;
51 }
52 Ok(ContainerCondition { conditions, span })
53 }
54 }
55 }
56}
57
58impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for ContainerConditionAnd<'s> {
59 fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
60 let keyword = input.parse::<Ident>()?;
61 if keyword.name.eq_ignore_ascii_case("and") {
62 let query_in_parens = input.parse::<QueryInParens>()?;
63 let span = Span {
64 start: keyword.span.start,
65 end: query_in_parens.span.end,
66 };
67 Ok(ContainerConditionAnd {
68 keyword,
69 query_in_parens,
70 span,
71 })
72 } else {
73 Err(Error {
74 kind: ErrorKind::ExpectContainerConditionAnd,
75 span: keyword.span,
76 })
77 }
78 }
79}
80
81impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for ContainerConditionNot<'s> {
82 fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
83 let keyword = input.parse::<Ident>()?;
84 if keyword.name.eq_ignore_ascii_case("not") {
85 let query_in_parens = input.parse::<QueryInParens>()?;
86 let span = Span {
87 start: keyword.span.start,
88 end: query_in_parens.span.end,
89 };
90 Ok(ContainerConditionNot {
91 keyword,
92 query_in_parens,
93 span,
94 })
95 } else {
96 Err(Error {
97 kind: ErrorKind::ExpectContainerConditionNot,
98 span: keyword.span,
99 })
100 }
101 }
102}
103
104impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for ContainerConditionOr<'s> {
105 fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
106 let keyword = input.parse::<Ident>()?;
107 if keyword.name.eq_ignore_ascii_case("or") {
108 let query_in_parens = input.parse::<QueryInParens>()?;
109 let span = Span {
110 start: keyword.span.start,
111 end: query_in_parens.span.end,
112 };
113 Ok(ContainerConditionOr {
114 keyword,
115 query_in_parens,
116 span,
117 })
118 } else {
119 Err(Error {
120 kind: ErrorKind::ExpectContainerConditionOr,
121 span: keyword.span,
122 })
123 }
124 }
125}
126
127impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for QueryInParens<'s> {
128 fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
129 if let Some((_, Span { start, .. })) = eat!(input, LParen) {
130 let kind = if let Ok(container_condition) = input.try_parse(ContainerCondition::parse) {
131 QueryInParensKind::ContainerCondition(container_condition)
132 } else {
133 QueryInParensKind::SizeFeature(Box::new(input.parse()?))
134 };
135 let (_, Span { end, .. }) = expect!(input, RParen);
136 Ok(QueryInParens {
137 kind,
138 span: Span { start, end },
139 })
140 } else {
141 let (style_keyword, ident_span) = expect!(input, Ident);
142 let keyword = style_keyword.name();
143 if keyword.eq_ignore_ascii_case("style") {
144 expect_without_ws_or_comments!(input, LParen);
145 let kind = input.parse().map(QueryInParensKind::StyleQuery)?;
146 let (_, Span { end, .. }) = expect!(input, RParen);
147 Ok(QueryInParens {
148 kind,
149 span: Span {
150 start: ident_span.start,
151 end,
152 },
153 })
154 } else if keyword.eq_ignore_ascii_case("scroll-state") {
155 expect_without_ws_or_comments!(input, LParen);
157 let kind = input
158 .parse()
159 .map(|media| QueryInParensKind::ScrollState(Box::new(media)))?;
160 let (_, Span { end, .. }) = expect!(input, RParen);
161 Ok(QueryInParens {
162 kind,
163 span: Span {
164 start: ident_span.start,
165 end,
166 },
167 })
168 } else {
169 Err(Error {
170 kind: ErrorKind::ExpectStyleQuery,
171 span: ident_span,
172 })
173 }
174 }
175 }
176}
177
178impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for StyleCondition<'s> {
179 fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
180 match &peek!(input).token {
181 Token::Ident(ident) if ident.name().eq_ignore_ascii_case("not") => {
182 let style_condition_not = input.parse::<StyleConditionNot>()?;
183 let span = style_condition_not.span.clone();
184 Ok(StyleCondition {
185 conditions: vec![StyleConditionKind::Not(style_condition_not)],
186 span,
187 })
188 }
189 _ => {
190 let first = input.parse::<StyleInParens>()?;
191 let mut span = first.span.clone();
192 let mut conditions = vec![StyleConditionKind::StyleInParens(first)];
193 if let Token::Ident(ident) = &peek!(input).token {
194 let name = ident.name();
195 if name.eq_ignore_ascii_case("and") {
196 loop {
197 conditions.push(StyleConditionKind::And(input.parse()?));
198 match &peek!(input).token {
199 Token::Ident(ident) if ident.name().eq_ignore_ascii_case("and") => {
200 }
201 _ => break,
202 }
203 }
204 } else if name.eq_ignore_ascii_case("or") {
205 loop {
206 conditions.push(StyleConditionKind::Or(input.parse()?));
207 match &peek!(input).token {
208 Token::Ident(ident) if ident.name().eq_ignore_ascii_case("or") => {}
209 _ => break,
210 }
211 }
212 }
213 }
214
215 if let Some(last) = conditions.last() {
216 span.end = last.span().end;
217 }
218 Ok(StyleCondition { conditions, span })
219 }
220 }
221 }
222}
223
224impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for StyleConditionAnd<'s> {
225 fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
226 let ident = input.parse::<Ident>()?;
227 if ident.name.eq_ignore_ascii_case("and") {
228 let style_in_parens = input.parse::<StyleInParens>()?;
229 let span = Span {
230 start: ident.span.start,
231 end: style_in_parens.span.end,
232 };
233 Ok(StyleConditionAnd {
234 keyword: ident,
235 style_in_parens,
236 span,
237 })
238 } else {
239 Err(Error {
240 kind: ErrorKind::ExpectStyleConditionAnd,
241 span: ident.span,
242 })
243 }
244 }
245}
246
247impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for StyleConditionNot<'s> {
248 fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
249 let keyword = input.parse::<Ident>()?;
250 if keyword.name.eq_ignore_ascii_case("not") {
251 let style_in_parens = input.parse::<StyleInParens>()?;
252 let span = Span {
253 start: keyword.span.start,
254 end: style_in_parens.span.end,
255 };
256 Ok(StyleConditionNot {
257 keyword,
258 style_in_parens,
259 span,
260 })
261 } else {
262 Err(Error {
263 kind: ErrorKind::ExpectStyleConditionNot,
264 span: keyword.span,
265 })
266 }
267 }
268}
269
270impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for StyleConditionOr<'s> {
271 fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
272 let keyword = input.parse::<Ident>()?;
273 if keyword.name.eq_ignore_ascii_case("or") {
274 let style_in_parens = input.parse::<StyleInParens>()?;
275 let span = Span {
276 start: keyword.span.start,
277 end: style_in_parens.span.end,
278 };
279 Ok(StyleConditionOr {
280 keyword,
281 style_in_parens,
282 span,
283 })
284 } else {
285 Err(Error {
286 kind: ErrorKind::ExpectStyleConditionOr,
287 span: keyword.span,
288 })
289 }
290 }
291}
292
293impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for StyleInParens<'s> {
294 fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
295 let (_, Span { start, .. }) = expect!(input, LParen);
296 let kind = input.parse()?;
297 let (_, Span { end, .. }) = expect!(input, RParen);
298 Ok(StyleInParens {
299 kind,
300 span: Span { start, end },
301 })
302 }
303}
304
305impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for StyleInParensKind<'s> {
306 fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
307 if let Ok(style_condition) = input.try_parse(StyleCondition::parse) {
308 Ok(StyleInParensKind::Condition(style_condition))
309 } else {
310 input.parse().map(StyleInParensKind::Feature)
311 }
312 }
313}
314
315impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for StyleQuery<'s> {
316 fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
317 if let Ok(condition) = input.try_parse(StyleCondition::parse) {
318 Ok(StyleQuery::Condition(condition))
319 } else {
320 let feature = input.parse().map(StyleQuery::Feature);
321 eat!(input, Semicolon);
322 feature
323 }
324 }
325}
326
327impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for ContainerPrelude<'s> {
329 fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
330 let name = input.try_parse(|parser| match parser.parse()? {
331 InterpolableIdent::Literal(ident)
332 if ident.name.eq_ignore_ascii_case("not")
333 || ident.name.eq_ignore_ascii_case("scroll-state") =>
334 {
335 Err(Error {
336 kind: ErrorKind::TryParseError,
337 span: ident.span,
338 })
339 }
340 InterpolableIdent::Literal(ident) if ident.name.eq_ignore_ascii_case("style") => {
341 match peek!(parser) {
342 TokenWithSpan {
343 token: Token::LParen(..),
344 span,
345 } if span.start == ident.span.end => Err(Error {
346 kind: ErrorKind::TryParseError,
347 span: ident.span,
348 }),
349 _ => Ok(InterpolableIdent::Literal(ident)),
350 }
351 }
352 ident => Ok(ident),
353 });
354 let condition = input.parse::<ContainerCondition>()?;
355 let mut span = condition.span().clone();
356 if let Ok(name) = &name {
357 span.start = name.span().start;
358 }
359 Ok(ContainerPrelude {
360 name: name.ok(),
361 condition,
362 span,
363 })
364 }
365}