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