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 match &input.cursor.peek()?.token {
19 Token::Ident(ident) if ident.name().eq_ignore_ascii_case("not") => {
20 let container_condition_not = input.parse::<ContainerConditionNot>()?;
21 let span = container_condition_not.span.clone();
22 Ok(ContainerCondition {
23 conditions: input.vec1(ContainerConditionKind::Not(container_condition_not)),
24 span,
25 })
26 }
27 _ => {
28 let first = input.parse::<QueryInParens>()?;
29 let mut span = first.span.clone();
30 let mut conditions = input.vec1(ContainerConditionKind::QueryInParens(first));
31 loop {
35 let kind = match &input.cursor.peek()?.token {
36 Token::Ident(ident) if ident.name().eq_ignore_ascii_case("and") => {
37 ContainerConditionKind::And(input.parse()?)
38 }
39 Token::Ident(ident) if ident.name().eq_ignore_ascii_case("or") => {
40 ContainerConditionKind::Or(input.parse()?)
41 }
42 Token::Ident(ident) if ident.name().eq_ignore_ascii_case("not") => {
43 ContainerConditionKind::Not(input.parse()?)
44 }
45 _ => break,
46 };
47 conditions.push(kind);
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> {
61 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
62 let keyword = input.parse::<Ident>()?;
63 if keyword.name.eq_ignore_ascii_case("and") {
64 let query_in_parens = input.parse::<QueryInParens>()?;
65 let span = Span { start: keyword.span.start, end: query_in_parens.span.end };
66 Ok(ContainerConditionAnd { keyword, query_in_parens, span })
67 } else {
68 Err(Error { kind: ErrorKind::ExpectContainerConditionAnd, span: keyword.span })
69 }
70 }
71}
72
73impl<'a> Parse<'a> for ContainerConditionNot<'a> {
75 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
76 let keyword = input.parse::<Ident>()?;
77 if keyword.name.eq_ignore_ascii_case("not") {
78 let query_in_parens = input.parse::<QueryInParens>()?;
79 let span = Span { start: keyword.span.start, end: query_in_parens.span.end };
80 Ok(ContainerConditionNot { keyword, query_in_parens, span })
81 } else {
82 Err(Error { kind: ErrorKind::ExpectContainerConditionNot, span: keyword.span })
83 }
84 }
85}
86
87impl<'a> Parse<'a> for ContainerConditionOr<'a> {
89 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
90 let keyword = input.parse::<Ident>()?;
91 if keyword.name.eq_ignore_ascii_case("or") {
92 let query_in_parens = input.parse::<QueryInParens>()?;
93 let span = Span { start: keyword.span.start, end: query_in_parens.span.end };
94 Ok(ContainerConditionOr { keyword, query_in_parens, span })
95 } else {
96 Err(Error { kind: ErrorKind::ExpectContainerConditionOr, span: keyword.span })
97 }
98 }
99}
100
101impl<'a> Parse<'a> for QueryInParens<'a> {
107 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
108 if let Some((_, Span { start, .. })) = input.cursor.eat_l_paren()? {
109 let kind = if let Ok(container_condition) = input.try_parse(ContainerCondition::parse) {
110 QueryInParensKind::ContainerCondition(container_condition)
111 } else {
112 let size_feature = input.parse()?;
113 QueryInParensKind::SizeFeature(input.alloc(size_feature))
114 };
115 let (_, Span { end, .. }) = input.cursor.expect_r_paren()?;
116 Ok(QueryInParens { kind, span: Span { start, end } })
117 } else {
118 let (style_keyword, ident_span) = input.cursor.expect_ident()?;
119 let keyword = style_keyword.name();
120 if keyword.eq_ignore_ascii_case("style") {
121 input.cursor.expect_l_paren_without_ws_or_comments()?;
122 let kind = input.parse().map(QueryInParensKind::StyleQuery)?;
123 let (_, Span { end, .. }) = input.cursor.expect_r_paren()?;
124 Ok(QueryInParens { kind, span: Span { start: ident_span.start, end } })
125 } else if keyword.eq_ignore_ascii_case("scroll-state") {
126 input.cursor.expect_l_paren_without_ws_or_comments()?;
128 let media = input.parse()?;
129 let kind = QueryInParensKind::ScrollState(input.alloc(media));
130 let (_, Span { end, .. }) = input.cursor.expect_r_paren()?;
131 Ok(QueryInParens { kind, span: Span { start: ident_span.start, end } })
132 } else {
133 Err(Error { kind: ErrorKind::ExpectStyleQuery, span: ident_span })
134 }
135 }
136 }
137}
138
139impl<'a> Parse<'a> for StyleCondition<'a> {
144 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
145 match &input.cursor.peek()?.token {
146 Token::Ident(ident) if ident.name().eq_ignore_ascii_case("not") => {
147 let style_condition_not = input.parse::<StyleConditionNot>()?;
148 let span = style_condition_not.span.clone();
149 Ok(StyleCondition {
150 conditions: input.vec1(StyleConditionKind::Not(style_condition_not)),
151 span,
152 })
153 }
154 _ => {
155 let first = input.parse::<StyleInParens>()?;
156 let mut span = first.span.clone();
157 let mut conditions = input.vec1(StyleConditionKind::StyleInParens(first));
158 if let Token::Ident(ident) = &input.cursor.peek()?.token {
159 let name = ident.name();
160 if name.eq_ignore_ascii_case("and") {
161 loop {
162 conditions.push(StyleConditionKind::And(input.parse()?));
163 match &input.cursor.peek()?.token {
164 Token::Ident(ident) if ident.name().eq_ignore_ascii_case("and") => {
165 }
166 _ => break,
167 }
168 }
169 } else if name.eq_ignore_ascii_case("or") {
170 loop {
171 conditions.push(StyleConditionKind::Or(input.parse()?));
172 match &input.cursor.peek()?.token {
173 Token::Ident(ident) if ident.name().eq_ignore_ascii_case("or") => {}
174 _ => break,
175 }
176 }
177 }
178 }
179
180 if let Some(last) = conditions.last() {
181 span.end = last.span().end;
182 }
183 Ok(StyleCondition { conditions, span })
184 }
185 }
186 }
187}
188
189impl<'a> Parse<'a> for StyleConditionAnd<'a> {
191 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
192 let ident = input.parse::<Ident>()?;
193 if ident.name.eq_ignore_ascii_case("and") {
194 let style_in_parens = input.parse::<StyleInParens>()?;
195 let span = Span { start: ident.span.start, end: style_in_parens.span.end };
196 Ok(StyleConditionAnd { keyword: ident, style_in_parens, span })
197 } else {
198 Err(Error { kind: ErrorKind::ExpectStyleConditionAnd, span: ident.span })
199 }
200 }
201}
202
203impl<'a> Parse<'a> for StyleConditionNot<'a> {
205 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
206 let keyword = input.parse::<Ident>()?;
207 if keyword.name.eq_ignore_ascii_case("not") {
208 let style_in_parens = input.parse::<StyleInParens>()?;
209 let span = Span { start: keyword.span.start, end: style_in_parens.span.end };
210 Ok(StyleConditionNot { keyword, style_in_parens, span })
211 } else {
212 Err(Error { kind: ErrorKind::ExpectStyleConditionNot, span: keyword.span })
213 }
214 }
215}
216
217impl<'a> Parse<'a> for StyleConditionOr<'a> {
219 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
220 let keyword = input.parse::<Ident>()?;
221 if keyword.name.eq_ignore_ascii_case("or") {
222 let style_in_parens = input.parse::<StyleInParens>()?;
223 let span = Span { start: keyword.span.start, end: style_in_parens.span.end };
224 Ok(StyleConditionOr { keyword, style_in_parens, span })
225 } else {
226 Err(Error { kind: ErrorKind::ExpectStyleConditionOr, span: keyword.span })
227 }
228 }
229}
230
231impl<'a> Parse<'a> for StyleInParens<'a> {
233 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
234 let (_, Span { start, .. }) = input.cursor.expect_l_paren()?;
235 let kind = input.parse()?;
236 let (_, Span { end, .. }) = input.cursor.expect_r_paren()?;
237 Ok(StyleInParens { kind, span: Span { start, end } })
238 }
239}
240
241impl<'a> Parse<'a> for StyleInParensKind<'a> {
243 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
244 if let Ok(style_condition) = input.try_parse(StyleCondition::parse) {
245 Ok(StyleInParensKind::Condition(style_condition))
246 } else {
247 input.parse().map(StyleInParensKind::Feature)
248 }
249 }
250}
251
252impl<'a> Parse<'a> for StyleQuery<'a> {
255 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
256 if let Ok(condition) = input.try_parse(StyleCondition::parse) {
257 Ok(StyleQuery::Condition(condition))
258 } else if let Ok(name) = input.try_parse(|p| {
259 let name = p.parse::<InterpolableIdent>()?;
261 match (&name, &p.cursor.peek()?.token) {
262 (InterpolableIdent::Literal(ident), Token::RParen(..))
263 if ident.name.starts_with("--") =>
264 {
265 Ok(name)
266 }
267 _ => {
268 let span = p.cursor.peek()?.span.clone();
269 Err(Error { kind: ErrorKind::TryParseError, span })
270 }
271 }
272 }) {
273 Ok(StyleQuery::FeatureName(name))
274 } else {
275 let feature = input.parse().map(StyleQuery::Feature);
276 input.cursor.eat_semicolon()?;
277 feature
278 }
279 }
280}
281
282impl<'a> Parse<'a> for ContainerPrelude<'a> {
287 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
288 let name = input.try_parse(|parser| match parser.parse()? {
289 InterpolableIdent::Literal(ident)
290 if ident.name.eq_ignore_ascii_case("not")
291 || ident.name.eq_ignore_ascii_case("scroll-state") =>
292 {
293 Err(Error { kind: ErrorKind::TryParseError, span: ident.span })
294 }
295 InterpolableIdent::Literal(ident) if ident.name.eq_ignore_ascii_case("style") => {
296 match parser.cursor.peek()? {
297 TokenWithSpan { token: Token::LParen(..), span }
298 if span.start == ident.span.end =>
299 {
300 Err(Error { kind: ErrorKind::TryParseError, span: ident.span })
301 }
302 _ => Ok(InterpolableIdent::Literal(ident)),
303 }
304 }
305 ident => Ok(ident),
306 });
307 let condition = input.parse::<ContainerCondition>()?;
308 let mut span = condition.span().clone();
309 if let Ok(name) = &name {
310 span.start = name.span().start;
311 }
312 Ok(ContainerPrelude { name: name.ok(), condition, span })
313 }
314}