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 = if let Ok(container_condition) = input.try_parse(ContainerCondition::parse) {
105 QueryInParensKind::ContainerCondition(container_condition)
106 } else {
107 let size_feature = input.parse()?;
108 QueryInParensKind::SizeFeature(input.alloc(size_feature))
109 };
110 let (_, Span { end, .. }) = input.cursor.expect_r_paren()?;
111 Ok(QueryInParens { kind, span: Span { start, end } })
112 } else {
113 let (style_keyword, ident_span) = input.cursor.expect_ident()?;
114 let keyword = style_keyword.name();
115 if keyword.eq_ignore_ascii_case("style") {
116 input.cursor.expect_l_paren_without_ws_or_comments()?;
117 let kind = input.parse().map(QueryInParensKind::StyleQuery)?;
118 let (_, Span { end, .. }) = input.cursor.expect_r_paren()?;
119 Ok(QueryInParens { kind, span: Span { start: ident_span.start, end } })
120 } else if keyword.eq_ignore_ascii_case("scroll-state") {
121 input.cursor.expect_l_paren_without_ws_or_comments()?;
123 let media = input.parse()?;
124 let kind = QueryInParensKind::ScrollState(input.alloc(media));
125 let (_, Span { end, .. }) = input.cursor.expect_r_paren()?;
126 Ok(QueryInParens { kind, span: Span { start: ident_span.start, end } })
127 } else {
128 Err(Error { kind: ErrorKind::ExpectStyleQuery, span: ident_span })
129 }
130 }
131 }
132}
133
134impl<'a> Parse<'a> for StyleCondition<'a> {
139 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
140 if input.cursor.peek()?.is_ident_name_eq_ignore_ascii_case(input.source, "not") {
141 let style_condition_not = input.parse::<StyleConditionNot>()?;
142 let span = style_condition_not.span;
143 Ok(StyleCondition {
144 conditions: input.vec1(StyleConditionKind::Not(style_condition_not)),
145 span,
146 })
147 } else {
148 let first = input.parse::<StyleInParens>()?;
149 let mut span = first.span;
150 let mut conditions = input.vec1(StyleConditionKind::StyleInParens(first));
151 let peek = input.cursor.peek()?;
152 if peek.ident(input.source).is_some() {
153 if peek.is_ident_name_eq_ignore_ascii_case(input.source, "and") {
154 loop {
155 conditions.push(StyleConditionKind::And(input.parse()?));
156 if !input
157 .cursor
158 .peek()?
159 .is_ident_name_eq_ignore_ascii_case(input.source, "and")
160 {
161 break;
162 }
163 }
164 } else if peek.is_ident_name_eq_ignore_ascii_case(input.source, "or") {
165 loop {
166 conditions.push(StyleConditionKind::Or(input.parse()?));
167 if !input
168 .cursor
169 .peek()?
170 .is_ident_name_eq_ignore_ascii_case(input.source, "or")
171 {
172 break;
173 }
174 }
175 }
176 }
177
178 if let Some(last) = conditions.last() {
179 span.end = last.span().end;
180 }
181 Ok(StyleCondition { conditions, span })
182 }
183 }
184}
185
186impl<'a> Parse<'a> for StyleConditionAnd<'a> {
188 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
189 let ident = input.parse::<Ident>()?;
190 if ident.name.eq_ignore_ascii_case("and") {
191 let style_in_parens = input.parse::<StyleInParens>()?;
192 let span = Span { start: ident.span.start, end: style_in_parens.span.end };
193 Ok(StyleConditionAnd { keyword: ident, style_in_parens, span })
194 } else {
195 Err(Error { kind: ErrorKind::ExpectStyleConditionAnd, span: ident.span })
196 }
197 }
198}
199
200impl<'a> Parse<'a> for StyleConditionNot<'a> {
202 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
203 let keyword = input.parse::<Ident>()?;
204 if keyword.name.eq_ignore_ascii_case("not") {
205 let style_in_parens = input.parse::<StyleInParens>()?;
206 let span = Span { start: keyword.span.start, end: style_in_parens.span.end };
207 Ok(StyleConditionNot { keyword, style_in_parens, span })
208 } else {
209 Err(Error { kind: ErrorKind::ExpectStyleConditionNot, span: keyword.span })
210 }
211 }
212}
213
214impl<'a> Parse<'a> for StyleConditionOr<'a> {
216 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
217 let keyword = input.parse::<Ident>()?;
218 if keyword.name.eq_ignore_ascii_case("or") {
219 let style_in_parens = input.parse::<StyleInParens>()?;
220 let span = Span { start: keyword.span.start, end: style_in_parens.span.end };
221 Ok(StyleConditionOr { keyword, style_in_parens, span })
222 } else {
223 Err(Error { kind: ErrorKind::ExpectStyleConditionOr, span: keyword.span })
224 }
225 }
226}
227
228impl<'a> Parse<'a> for StyleInParens<'a> {
230 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
231 let (_, Span { start, .. }) = input.cursor.expect_l_paren()?;
232 let kind = input.parse()?;
233 let (_, Span { end, .. }) = input.cursor.expect_r_paren()?;
234 Ok(StyleInParens { kind, span: Span { start, end } })
235 }
236}
237
238impl<'a> Parse<'a> for StyleInParensKind<'a> {
240 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
241 if let Ok(style_condition) = input.try_parse(StyleCondition::parse) {
242 Ok(StyleInParensKind::Condition(style_condition))
243 } else {
244 input.parse().map(StyleInParensKind::Feature)
245 }
246 }
247}
248
249impl<'a> Parse<'a> for StyleQuery<'a> {
252 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
253 if let Ok(condition) = input.try_parse(StyleCondition::parse) {
254 Ok(StyleQuery::Condition(condition))
255 } else if let Ok(name) = input.try_parse(|p| {
256 let name = p.parse::<InterpolableIdent>()?;
258 match (&name, &p.cursor.peek()?.token) {
259 (InterpolableIdent::Literal(ident), Token::RParen(..))
260 if ident.name.starts_with("--") =>
261 {
262 Ok(name)
263 }
264 _ => {
265 let span = p.cursor.peek()?.span;
266 Err(Error { kind: ErrorKind::TryParseError, span })
267 }
268 }
269 }) {
270 Ok(StyleQuery::FeatureName(name))
271 } else {
272 let feature = input.parse().map(StyleQuery::Feature);
273 input.cursor.eat_semicolon()?;
274 feature
275 }
276 }
277}
278
279impl<'a> Parse<'a> for ContainerPrelude<'a> {
284 fn parse(input: &mut Parser<'a>) -> PResult<Self> {
285 let name = input.try_parse(|parser| match parser.parse()? {
286 InterpolableIdent::Literal(ident)
287 if ident.name.eq_ignore_ascii_case("not")
288 || ident.name.eq_ignore_ascii_case("scroll-state") =>
289 {
290 Err(Error { kind: ErrorKind::TryParseError, span: ident.span })
291 }
292 InterpolableIdent::Literal(ident) if ident.name.eq_ignore_ascii_case("style") => {
293 match parser.cursor.peek()? {
294 TokenWithSpan { token: Token::LParen(..), span }
295 if span.start == ident.span.end =>
296 {
297 Err(Error { kind: ErrorKind::TryParseError, span: ident.span })
298 }
299 _ => Ok(InterpolableIdent::Literal(ident)),
300 }
301 }
302 ident => Ok(ident),
303 });
304 let condition = input.parse::<ContainerCondition>()?;
305 let mut span = *condition.span();
306 if let Ok(name) = &name {
307 span.start = name.span().start;
308 }
309 Ok(ContainerPrelude { name: name.ok(), condition, span })
310 }
311}