oxc_css_parser/parser/at_rule/
container.rs1use super::Parser;
2use crate::{
3 Parse,
4 ast::*,
5 error::{Error, ErrorKind, PResult},
6 pos::Span,
7 tokenizer::{Token, TokenWithSpan},
8};
9
10impl<'a> Parse<'a> for ContainerCondition<'a> {
17 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
18 if input.cursor.peek()?.is_ident_name_eq_ignore_ascii_case(input.source, "not") {
19 let container_condition_not = input.parse::<ContainerConditionNot>()?;
20 let span = container_condition_not.span;
21 Ok(ContainerCondition {
22 conditions: input.vec1(ContainerConditionKind::Not(container_condition_not)),
23 span,
24 })
25 } else {
26 let first = input.parse::<QueryInParens>()?;
27 let mut span = first.span;
28 let mut conditions = input.vec1(ContainerConditionKind::QueryInParens(first));
29 loop {
33 let peek = input.cursor.peek()?;
34 let kind = if peek.is_ident_name_eq_ignore_ascii_case(input.source, "and") {
35 ContainerConditionKind::And(input.parse()?)
36 } else if peek.is_ident_name_eq_ignore_ascii_case(input.source, "or") {
37 ContainerConditionKind::Or(input.parse()?)
38 } else if peek.is_ident_name_eq_ignore_ascii_case(input.source, "not") {
39 ContainerConditionKind::Not(input.parse()?)
40 } else {
41 break;
42 };
43 conditions.push(kind);
44 }
45
46 if let Some(last) = conditions.last() {
47 span.end = last.span().end;
48 }
49 Ok(ContainerCondition { conditions, span })
50 }
51 }
52}
53
54impl<'a> Parse<'a> for ContainerConditionAnd<'a> {
56 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
57 let keyword = input.parse::<Ident>()?;
58 if keyword.name.eq_ignore_ascii_case("and") {
59 let query_in_parens = input.parse::<QueryInParens>()?;
60 let span = Span { start: keyword.span.start, end: query_in_parens.span.end };
61 Ok(ContainerConditionAnd { keyword, query_in_parens, span })
62 } else {
63 Err(Error { kind: ErrorKind::ExpectContainerConditionAnd, span: keyword.span })
64 }
65 }
66}
67
68impl<'a> Parse<'a> for ContainerConditionNot<'a> {
70 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
71 let keyword = input.parse::<Ident>()?;
72 if keyword.name.eq_ignore_ascii_case("not") {
73 let query_in_parens = input.parse::<QueryInParens>()?;
74 let span = Span { start: keyword.span.start, end: query_in_parens.span.end };
75 Ok(ContainerConditionNot { keyword, query_in_parens, span })
76 } else {
77 Err(Error { kind: ErrorKind::ExpectContainerConditionNot, span: keyword.span })
78 }
79 }
80}
81
82impl<'a> Parse<'a> for ContainerConditionOr<'a> {
84 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
85 let keyword = input.parse::<Ident>()?;
86 if keyword.name.eq_ignore_ascii_case("or") {
87 let query_in_parens = input.parse::<QueryInParens>()?;
88 let span = Span { start: keyword.span.start, end: query_in_parens.span.end };
89 Ok(ContainerConditionOr { keyword, query_in_parens, span })
90 } else {
91 Err(Error { kind: ErrorKind::ExpectContainerConditionOr, span: keyword.span })
92 }
93 }
94}
95
96impl<'a> Parse<'a> for QueryInParens<'a> {
102 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
103 if let Some((_, Span { start, .. })) = input.cursor.eat_l_paren()? {
104 let (kind, end) = if let Ok((condition, end)) = input.try_parse(|p| {
108 let condition = ContainerCondition::parse(p)?;
109 let (_, Span { end, .. }) = p.cursor.expect_r_paren()?;
110 Ok((condition, end))
111 }) {
112 (QueryInParensKind::ContainerCondition(condition), end)
113 } else if let Ok((size_feature, end)) = input.try_parse(|p| {
114 let size_feature = MediaFeature::parse(p)?;
115 let (_, Span { end, .. }) = p.cursor.expect_r_paren()?;
116 Ok((size_feature, end))
117 }) {
118 (QueryInParensKind::SizeFeature(input.alloc(size_feature)), end)
119 } else {
120 let tokens = input.parse_tokens_in_parens()?;
123 let (_, Span { end, .. }) = input.cursor.expect_r_paren()?;
124 (QueryInParensKind::GeneralEnclosed(tokens), end)
125 };
126 Ok(QueryInParens { kind, span: Span { start, end } })
127 } else {
128 let (style_keyword, ident_span) = input.cursor.expect_ident()?;
129 let keyword = style_keyword.name();
130 if keyword.eq_ignore_ascii_case("style") {
131 input.cursor.expect_l_paren_without_ws_or_comments()?;
132 let kind = input.parse().map(QueryInParensKind::StyleQuery)?;
133 let (_, Span { end, .. }) = input.cursor.expect_r_paren()?;
134 Ok(QueryInParens { kind, span: Span { start: ident_span.start, end } })
135 } else if keyword.eq_ignore_ascii_case("scroll-state") {
136 input.cursor.expect_l_paren_without_ws_or_comments()?;
138 let media = input.parse()?;
139 let kind = QueryInParensKind::ScrollState(input.alloc(media));
140 let (_, Span { end, .. }) = input.cursor.expect_r_paren()?;
141 Ok(QueryInParens { kind, span: Span { start: ident_span.start, end } })
142 } else if matches!(input.cursor.peek()?, TokenWithSpan { token: Token::LParen(..), span } if span.start == ident_span.end)
143 {
144 input.cursor.expect_l_paren()?;
148 let kind = QueryInParensKind::GeneralEnclosed(input.parse_tokens_in_parens()?);
149 let (_, Span { end, .. }) = input.cursor.expect_r_paren()?;
150 Ok(QueryInParens { kind, span: Span { start: ident_span.start, end } })
151 } else {
152 Err(Error { kind: ErrorKind::ExpectStyleQuery, span: ident_span })
153 }
154 }
155 }
156}
157
158impl<'a> Parse<'a> for StyleCondition<'a> {
163 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
164 if input.cursor.peek()?.is_ident_name_eq_ignore_ascii_case(input.source, "not") {
165 let style_condition_not = input.parse::<StyleConditionNot>()?;
166 let span = style_condition_not.span;
167 Ok(StyleCondition {
168 conditions: input.vec1(StyleConditionKind::Not(style_condition_not)),
169 span,
170 })
171 } else {
172 let first = input.parse::<StyleInParens>()?;
173 let mut span = first.span;
174 let mut conditions = input.vec1(StyleConditionKind::StyleInParens(first));
175 let peek = input.cursor.peek()?;
176 if peek.ident(input.source).is_some() {
177 if peek.is_ident_name_eq_ignore_ascii_case(input.source, "and") {
178 loop {
179 conditions.push(StyleConditionKind::And(input.parse()?));
180 if !input
181 .cursor
182 .peek()?
183 .is_ident_name_eq_ignore_ascii_case(input.source, "and")
184 {
185 break;
186 }
187 }
188 } else if peek.is_ident_name_eq_ignore_ascii_case(input.source, "or") {
189 loop {
190 conditions.push(StyleConditionKind::Or(input.parse()?));
191 if !input
192 .cursor
193 .peek()?
194 .is_ident_name_eq_ignore_ascii_case(input.source, "or")
195 {
196 break;
197 }
198 }
199 }
200 }
201
202 if let Some(last) = conditions.last() {
203 span.end = last.span().end;
204 }
205 Ok(StyleCondition { conditions, span })
206 }
207 }
208}
209
210impl<'a> Parse<'a> for StyleConditionAnd<'a> {
212 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
213 let ident = input.parse::<Ident>()?;
214 if ident.name.eq_ignore_ascii_case("and") {
215 let style_in_parens = input.parse::<StyleInParens>()?;
216 let span = Span { start: ident.span.start, end: style_in_parens.span.end };
217 Ok(StyleConditionAnd { keyword: ident, style_in_parens, span })
218 } else {
219 Err(Error { kind: ErrorKind::ExpectStyleConditionAnd, span: ident.span })
220 }
221 }
222}
223
224impl<'a> Parse<'a> for StyleConditionNot<'a> {
226 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
227 let keyword = input.parse::<Ident>()?;
228 if keyword.name.eq_ignore_ascii_case("not") {
229 let style_in_parens = input.parse::<StyleInParens>()?;
230 let span = Span { start: keyword.span.start, end: style_in_parens.span.end };
231 Ok(StyleConditionNot { keyword, style_in_parens, span })
232 } else {
233 Err(Error { kind: ErrorKind::ExpectStyleConditionNot, span: keyword.span })
234 }
235 }
236}
237
238impl<'a> Parse<'a> for StyleConditionOr<'a> {
240 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
241 let keyword = input.parse::<Ident>()?;
242 if keyword.name.eq_ignore_ascii_case("or") {
243 let style_in_parens = input.parse::<StyleInParens>()?;
244 let span = Span { start: keyword.span.start, end: style_in_parens.span.end };
245 Ok(StyleConditionOr { keyword, style_in_parens, span })
246 } else {
247 Err(Error { kind: ErrorKind::ExpectStyleConditionOr, span: keyword.span })
248 }
249 }
250}
251
252impl<'a> Parse<'a> for StyleInParens<'a> {
254 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
255 let (_, Span { start, .. }) = input.cursor.expect_l_paren()?;
256 let kind = input.parse()?;
257 let (_, Span { end, .. }) = input.cursor.expect_r_paren()?;
258 Ok(StyleInParens { kind, span: Span { start, end } })
259 }
260}
261
262impl<'a> Parse<'a> for StyleInParensKind<'a> {
264 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
265 if let Ok(style_condition) = input.try_parse(StyleCondition::parse) {
266 Ok(StyleInParensKind::Condition(style_condition))
267 } else {
268 input.parse().map(StyleInParensKind::Feature)
269 }
270 }
271}
272
273impl<'a> Parse<'a> for StyleQuery<'a> {
276 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
277 if let Ok(condition) = input.try_parse(StyleCondition::parse) {
278 Ok(StyleQuery::Condition(condition))
279 } else if let Ok(name) = input.try_parse(|p| {
280 let name = p.parse::<InterpolableIdent>()?;
282 match (&name, &p.cursor.peek()?.token) {
283 (InterpolableIdent::Literal(ident), Token::RParen(..))
284 if ident.name.starts_with("--") =>
285 {
286 Ok(name)
287 }
288 _ => {
289 let span = p.cursor.peek()?.span;
290 Err(Error { kind: ErrorKind::TryParseError, span })
291 }
292 }
293 }) {
294 Ok(StyleQuery::FeatureName(name))
295 } else {
296 let feature = input.parse().map(StyleQuery::Feature);
297 input.cursor.eat_semicolon()?;
298 feature
299 }
300 }
301}
302
303impl<'a> Parse<'a> for ContainerPrelude<'a> {
308 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
309 let name = input.try_parse(|parser| match parser.parse()? {
310 InterpolableIdent::Literal(ident)
311 if ident.name.eq_ignore_ascii_case("not")
312 || ident.name.eq_ignore_ascii_case("scroll-state") =>
313 {
314 Err(Error { kind: ErrorKind::TryParseError, span: ident.span })
315 }
316 InterpolableIdent::Literal(ident) => match parser.cursor.peek()? {
319 TokenWithSpan { token: Token::LParen(..), span }
320 if span.start == ident.span.end =>
321 {
322 Err(Error { kind: ErrorKind::TryParseError, span: ident.span })
323 }
324 _ => Ok(InterpolableIdent::Literal(ident)),
325 },
326 ident => Ok(ident),
327 });
328 let name = name.ok();
331 let condition = if name.is_some() {
332 input.try_parse(ContainerCondition::parse).ok()
333 } else {
334 Some(input.parse::<ContainerCondition>()?)
335 };
336 let span = match (&name, &condition) {
337 (Some(name), Some(condition)) => {
338 Span { start: name.span().start, end: condition.span.end }
339 }
340 (Some(name), None) => *name.span(),
341 (None, Some(condition)) => condition.span,
342 (None, None) => unreachable!(),
343 };
344 Ok(ContainerPrelude { name, condition, span })
345 }
346}