css_parse/traits/lists/feature_condition_list.rs
1use crate::{Cursor, Parse, Parser, Peek, Result, Vec, token_macros::Ident};
2
3/// This trait can be used for AST nodes representing a list of "Feature Conditions". This is an amalgamation of
4/// [Supports Conditions][1], [Media Conditions][2], and [Container Queries][3]
5/// This is an implementation of [`<at-rule-list>`][1].
6///
7/// Looking at `<supports-condition>` and `<container-query>` we can se almost identical grammars (eliding some tokens
8/// for brevity):
9///
10/// ```md
11/// <supports-condition>
12/// │├─╮─ <ident-token "not"> ─ <supports-in-parens> ──────────────────────────────╭──┤│
13/// ╰─ <supports-in-parens> ─╮─╭─ <ident-token "and"> ─ <supports-in-parens> ─╮─┤
14/// │ ╰──────────────────────────────────────────────╯ │
15/// ├─╭─ <ident-token "or"> ─ <supports-in-parens> ─╮──┤
16/// │ ╰─────────────────────────────────────────────╯ │
17/// ╰──────────────────────────────────────────────────╯
18///
19/// <container-query>
20/// │├─╮─ <ident-token "not"> ─ <query-in-parens> ───────────────────────────╭──┤│
21/// ╰─ <supports-in-parens> ─╮─╭─ <ident-token "and"> ─ <supports-in-parens> ─╮─┤
22/// │ ╰──────────────────────────────────────────────╯ │
23/// ├─╭─ <ident-token "or"> ─ <supports-in-parens> ─╮──┤
24/// │ ╰─────────────────────────────────────────────╯ │
25/// ╰──────────────────────────────────────────────────╯
26///
27/// <media-condition>
28/// │├─╮─ <ident-token "not"> ─ <media-in-parens> ───────────────────────────╭──┤│
29/// ╰─ <media-in-parens> ─╮─╭─ <ident-token "and"> ─ <media-in-parens> ─╮─┤
30/// │ ╰───────────────────────────────────────────╯ │
31/// │─╭─ <ident-token "or"> ─ <media-in-parens> ─╮──│
32/// │ ╰──────────────────────────────────────────╯ │
33/// ╰───────────────────────────────────────────────╯
34/// ```
35///
36/// The key difference between each of these is their own `<*-in-parens>` tokens. Thus they could all be defined as:
37///
38/// ```md
39/// <condition-prelude-list>
40/// │├─╮─ <ident-token "not"> ─ <feature> ───────────────────╭──┤│
41/// ╰─ <feature> ─╮─╭─ <ident-token "and"> ─ <feature> ─╮─┤
42/// │ ╰───────────────────────────────────╯ │
43/// │─╭─ <ident-token "or"> ─ <feature> ─╮──│
44/// │ ╰──────────────────────────────────╯ │
45/// ╰───────────────────────────────────────╯
46/// ```
47///
48/// [1]: https://drafts.csswg.org/css-conditional-3/#typedef-supports-condition
49/// [2]: https://drafts.csswg.org/mediaqueries/#media-condition
50/// [3]: https://drafts.csswg.org/css-conditional-5/#typedef-container-query
51pub trait FeatureConditionList<'a>: Sized + Parse<'a>
52where
53 Self: 'a,
54{
55 type FeatureCondition: Sized + Parse<'a>;
56
57 fn keyword_is_not<I>(p: &Parser<'a, I>, c: Cursor) -> bool
58 where
59 I: Iterator<Item = Cursor> + Clone;
60 fn keyword_is_or<I>(p: &Parser<'a, I>, c: Cursor) -> bool
61 where
62 I: Iterator<Item = Cursor> + Clone;
63 fn keyword_is_and<I>(p: &Parser<'a, I>, c: Cursor) -> bool
64 where
65 I: Iterator<Item = Cursor> + Clone;
66
67 fn build_is(feature: Self::FeatureCondition) -> Self;
68 fn build_not(keyword: Ident, feature: Self::FeatureCondition) -> Self;
69 fn build_and(features: Vec<'a, (Self::FeatureCondition, Option<Ident>)>) -> Self;
70 fn build_or(features: Vec<'a, (Self::FeatureCondition, Option<Ident>)>) -> Self;
71
72 fn parse_condition<I>(p: &mut Parser<'a, I>) -> Result<Self>
73 where
74 I: Iterator<Item = Cursor> + Clone,
75 {
76 let c = p.peek_n(1);
77 if Ident::peek(p, c) && Self::keyword_is_not(p, c) {
78 return Ok(Self::build_not(p.parse::<Ident>()?, p.parse::<Self::FeatureCondition>()?));
79 }
80 let mut feature = p.parse::<Self::FeatureCondition>()?;
81 let c = p.peek_n(1);
82 if Ident::peek(p, c) {
83 if Self::keyword_is_and(p, c) {
84 let mut features = Vec::new_in(p.alloc());
85 let mut keyword = p.parse::<Ident>()?;
86 loop {
87 features.push((feature, Some(keyword)));
88 feature = p.parse::<Self::FeatureCondition>()?;
89 let c = p.peek_n(1);
90 if !(Ident::peek(p, c) && Self::keyword_is_and(p, c)) {
91 features.push((feature, None));
92 return Ok(Self::build_and(features));
93 }
94 keyword = p.parse::<Ident>()?
95 }
96 } else if Self::keyword_is_or(p, c) {
97 let mut features = Vec::new_in(p.alloc());
98 let mut keyword = p.parse::<Ident>()?;
99 loop {
100 features.push((feature, Some(keyword)));
101 feature = p.parse::<Self::FeatureCondition>()?;
102 let c = p.peek_n(1);
103 if !(Ident::peek(p, c) && Self::keyword_is_or(p, c)) {
104 features.push((feature, None));
105 return Ok(Self::build_or(features));
106 }
107 keyword = p.parse::<Ident>()?
108 }
109 }
110 }
111 Ok(Self::build_is(feature))
112 }
113}