1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
use crateCursor;
use crate::;
use DynAtomSet;
/// This trait provides an implementation for parsing a ["Media Feature" in the "Boolean" context][1]. This is
/// complementary to the other media features: [RangedFeature][crate::RangedFeature] and
/// [DiscreteFeature][crate::DiscreteFeature].
///
/// [1]: https://drafts.csswg.org/mediaqueries/#boolean-context
///
/// Rather than implementing this trait on an enum, use the [boolean_feature!][crate::boolean_feature] macro which
/// expands to define the enum and necessary traits ([Parse][crate::Parse], this trait, and
/// [ToCursors][crate::ToCursors]) in a single macro call.
///
/// It does not implement [Parse][crate::Parse], but provides
/// `parse_boolean_feature(&mut Parser<'a>, name: &str) -> Result<Self>`, which can make for a trivial
/// [Parse][crate::Parse] implementation. The `name: &str` parameter refers to the `<feature-name>` token, which will
/// be parsed as an Ident.
///
/// CSS defines the Media Feature generally as:
///
/// ```md
/// │├─ "(" ─╮─ <feature-name> ─ ":" ─ <value> ─╭─ ")" ─┤│
/// ├─ <feature-name> ─────────────────┤
/// ╰─ <ranged-feature> ───────────────╯
///
/// ```
///
/// The [RangedFeature][crate::RangedFeature] trait provides algorithms for parsing `<ranged-feature>` productions, but
/// boolean features use the other two productions, with some rules around the `<value>`.
///
/// A boolean media query:
///
/// - Can omit the the `:` and `<value>`.
/// - Must allow any token as the `<value>`, but the `<dimension>` of `0`, `<number>` of `0` and `<ident>` of `none`
/// will mean the query evaluates to false.
///
/// Given these, this trait parses as:
///
/// ```md
/// <boolean-feature>
/// │├─ "(" ─╮─ <feature-name> ─ ":" ─ <any> ─╭─ ")" ─┤│
/// ╰─ <feature-name> ───────────────╯
///
/// ```
///
/// This macro expands to define an enum which already implements [Parse][crate::Parse] and [BooleanFeature], for a
/// one-liner definition of a [BooleanFeature].
///
/// # Example
///
/// ```
/// use css_lexer::*;
/// use css_parse::*;
/// use csskit_derives::*;
/// use derive_atom_set::*;
/// use bumpalo::Bump;
///
/// #[derive(Debug, Default, AtomSet, Copy, Clone, PartialEq)]
/// pub enum MyAtomSet {
/// #[default]
/// _None,
/// TestFeature,
/// }
/// impl MyAtomSet {
/// const ATOMS: MyAtomSet = MyAtomSet::_None;
/// }
///
/// // Define the Boolean Feature.
/// boolean_feature! {
/// /// A boolean media feature: `(test-feature)`
/// #[derive(ToCursors, ToSpan, Debug)]
/// pub enum TestFeature{MyAtomSet::TestFeature}
/// }
///
/// // Test!
/// let allocator = Bump::new();
/// let source_text = "(test-feature)";
/// let lexer = Lexer::new( &MyAtomSet::ATOMS, &source_text);
/// let mut p = Parser::new(&allocator, &source_text, lexer);
/// let result = p.parse_entirely::<TestFeature>();
/// assert!(matches!(result.output, Some(TestFeature::Bare(open, ident, close))));
///
/// let source_text = "(test-feature: none)";
/// let lexer = Lexer::new(&MyAtomSet::ATOMS, &source_text);
/// let mut p = Parser::new(&allocator, &source_text, lexer);
/// let result = p.parse_entirely::<TestFeature>();
/// assert!(matches!(result.output, Some(TestFeature::WithValue(open, ident, colon, any, close))));
/// ```
///