css_ast/rules/container/
features.rs

1use super::super::prelude::*;
2use crate::{types::Ratio, units::Length};
3use css_parse::{discrete_feature, ranged_feature};
4#[cfg(feature = "visitable")]
5use csskit_proc_macro::visit;
6
7ranged_feature!(
8	#[derive(ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
9	#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
10	#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
11	pub enum WidthContainerFeature{CssAtomSet::Width, Length}
12);
13
14ranged_feature!(
15	#[derive(ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
16	#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
17	#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
18	pub enum HeightContainerFeature{CssAtomSet::Height, Length}
19);
20
21ranged_feature!(
22	#[derive(ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
23	#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
24	#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
25	pub enum InlineSizeContainerFeature{CssAtomSet::InlineSize, Length}
26);
27
28ranged_feature!(
29	#[derive(ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
30	#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
31	#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
32	pub enum BlockSizeContainerFeature{CssAtomSet::BlockSize, Length}
33);
34
35ranged_feature!(
36	#[derive(ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
37	#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
38	#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
39	pub enum AspectRatioContainerFeature{CssAtomSet::AspectRatio, Ratio}
40);
41
42#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
43#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
44#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(skip))]
45pub enum OrientationContainerFeatureKeyword {
46	#[atom(CssAtomSet::Portrait)]
47	Portrait(T![Ident]),
48	#[atom(CssAtomSet::Landscape)]
49	Landscape(T![Ident]),
50}
51
52discrete_feature!(
53	#[derive(ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
54	#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
55	#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
56	pub enum OrientationContainerFeature{CssAtomSet::Orientation, OrientationContainerFeatureKeyword}
57);
58
59#[derive(ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
60#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
61#[cfg_attr(feature = "visitable", visit)]
62pub enum StyleQuery<'a> {
63	Is(Declaration<'a, StyleValue<'a>, CssMetadata>),
64	Not(T![Ident], Declaration<'a, StyleValue<'a>, CssMetadata>),
65	And(Vec<'a, (Declaration<'a, StyleValue<'a>, CssMetadata>, Option<T![Ident]>)>),
66	Or(Vec<'a, (Declaration<'a, StyleValue<'a>, CssMetadata>, Option<T![Ident]>)>),
67}
68
69impl<'a> FeatureConditionList<'a> for StyleQuery<'a> {
70	type FeatureCondition = Declaration<'a, StyleValue<'a>, CssMetadata>;
71	fn keyword_is_not<I>(p: &Parser<'a, I>, c: Cursor) -> bool
72	where
73		I: Iterator<Item = Cursor> + Clone,
74	{
75		p.equals_atom(c, &CssAtomSet::Not)
76	}
77	fn keyword_is_and<I>(p: &Parser<'a, I>, c: Cursor) -> bool
78	where
79		I: Iterator<Item = Cursor> + Clone,
80	{
81		p.equals_atom(c, &CssAtomSet::And)
82	}
83	fn keyword_is_or<I>(p: &Parser<'a, I>, c: Cursor) -> bool
84	where
85		I: Iterator<Item = Cursor> + Clone,
86	{
87		p.equals_atom(c, &CssAtomSet::Or)
88	}
89	fn build_is(feature: Self::FeatureCondition) -> Self {
90		Self::Is(feature)
91	}
92	fn build_not(keyword: T![Ident], feature: Self::FeatureCondition) -> Self {
93		Self::Not(keyword, feature)
94	}
95	fn build_and(feature: Vec<'a, (Self::FeatureCondition, Option<T![Ident]>)>) -> Self {
96		Self::And(feature)
97	}
98	fn build_or(feature: Vec<'a, (Self::FeatureCondition, Option<T![Ident]>)>) -> Self {
99		Self::Or(feature)
100	}
101}
102
103impl<'a> Parse<'a> for StyleQuery<'a> {
104	fn parse<I>(p: &mut Parser<'a, I>) -> ParserResult<Self>
105	where
106		I: Iterator<Item = Cursor> + Clone,
107	{
108		Self::parse_condition(p)
109	}
110}
111
112#[cfg(feature = "visitable")]
113impl<'a> VisitableTrait for StyleQuery<'a> {
114	fn accept<V: Visit>(&self, v: &mut V) {
115		v.visit_style_query(self);
116		match self {
117			Self::Is(feature) => feature.accept(v),
118			Self::Not(_, feature) => feature.accept(v),
119			Self::And(features) => {
120				for (feature, _) in features {
121					feature.accept(v);
122				}
123			}
124			Self::Or(features) => {
125				for (feature, _) in features {
126					feature.accept(v);
127				}
128			}
129		}
130	}
131}
132
133#[cfg(feature = "visitable")]
134impl<'a> VisitableMut for StyleQuery<'a> {
135	fn accept_mut<V: VisitMut>(&mut self, v: &mut V) {
136		v.visit_style_query(self);
137		match self {
138			Self::Is(feature) => feature.accept_mut(v),
139			Self::Not(_, feature) => feature.accept_mut(v),
140			Self::And(features) => {
141				for (feature, _) in features {
142					feature.accept_mut(v);
143				}
144			}
145			Self::Or(features) => {
146				for (feature, _) in features {
147					feature.accept_mut(v);
148				}
149			}
150		}
151	}
152}
153
154#[derive(ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
155#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
156#[cfg_attr(feature = "visitable", visit)]
157pub enum ScrollStateQuery<'a> {
158	Is(ScrollStateFeature),
159	Not(T![Ident], ScrollStateFeature),
160	And(Vec<'a, (ScrollStateFeature, Option<T![Ident]>)>),
161	Or(Vec<'a, (ScrollStateFeature, Option<T![Ident]>)>),
162}
163
164impl<'a> FeatureConditionList<'a> for ScrollStateQuery<'a> {
165	type FeatureCondition = ScrollStateFeature;
166	fn keyword_is_not<I>(p: &Parser<'a, I>, c: Cursor) -> bool
167	where
168		I: Iterator<Item = Cursor> + Clone,
169	{
170		p.equals_atom(c, &CssAtomSet::Not)
171	}
172	fn keyword_is_and<I>(p: &Parser<'a, I>, c: Cursor) -> bool
173	where
174		I: Iterator<Item = Cursor> + Clone,
175	{
176		p.equals_atom(c, &CssAtomSet::And)
177	}
178	fn keyword_is_or<I>(p: &Parser<'a, I>, c: Cursor) -> bool
179	where
180		I: Iterator<Item = Cursor> + Clone,
181	{
182		p.equals_atom(c, &CssAtomSet::Or)
183	}
184	fn build_is(feature: ScrollStateFeature) -> Self {
185		Self::Is(feature)
186	}
187	fn build_not(keyword: T![Ident], feature: ScrollStateFeature) -> Self {
188		Self::Not(keyword, feature)
189	}
190	fn build_and(feature: Vec<'a, (ScrollStateFeature, Option<T![Ident]>)>) -> Self {
191		Self::And(feature)
192	}
193	fn build_or(feature: Vec<'a, (ScrollStateFeature, Option<T![Ident]>)>) -> Self {
194		Self::Or(feature)
195	}
196}
197
198impl<'a> Parse<'a> for ScrollStateQuery<'a> {
199	fn parse<I>(p: &mut Parser<'a, I>) -> ParserResult<Self>
200	where
201		I: Iterator<Item = Cursor> + Clone,
202	{
203		Self::parse_condition(p)
204	}
205}
206
207#[cfg(feature = "visitable")]
208impl<'a> VisitableTrait for ScrollStateQuery<'a> {
209	fn accept<V: Visit>(&self, v: &mut V) {
210		match self {
211			Self::Is(feature) => feature.accept(v),
212			Self::Not(_, feature) => feature.accept(v),
213			Self::And(features) => {
214				for (feature, _) in features {
215					feature.accept(v);
216				}
217			}
218			Self::Or(features) => {
219				for (feature, _) in features {
220					feature.accept(v);
221				}
222			}
223		}
224	}
225}
226
227#[cfg(feature = "visitable")]
228impl<'a> VisitableMut for ScrollStateQuery<'a> {
229	fn accept_mut<V: VisitMut>(&mut self, v: &mut V) {
230		match self {
231			Self::Is(feature) => feature.accept_mut(v),
232			Self::Not(_, feature) => feature.accept_mut(v),
233			Self::And(features) => {
234				for (feature, _) in features {
235					feature.accept_mut(v);
236				}
237			}
238			Self::Or(features) => {
239				for (feature, _) in features {
240					feature.accept_mut(v);
241				}
242			}
243		}
244	}
245}
246
247#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
248#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
249#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
250pub enum ScrollStateFeature {
251	Scrollable(ScrollableScrollStateFeature),
252	Snapped(SnappedScrollStateFeature),
253	Stuck(StuckScrollStateFeature),
254}
255
256discrete_feature!(
257	#[derive(Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
258	#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
259	#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
260	pub enum ScrollableScrollStateFeature{CssAtomSet::Scrollable, ScrollableScrollStateFeatureKeyword}
261);
262
263#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
264#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
265#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(skip))]
266pub enum ScrollableScrollStateFeatureKeyword {
267	#[atom(CssAtomSet::None)]
268	None(T![Ident]),
269	#[atom(CssAtomSet::Top)]
270	Top(T![Ident]),
271	#[atom(CssAtomSet::Right)]
272	Right(T![Ident]),
273	#[atom(CssAtomSet::Bottom)]
274	Bottom(T![Ident]),
275	#[atom(CssAtomSet::Left)]
276	Left(T![Ident]),
277	#[atom(CssAtomSet::BlockStart)]
278	BlockStart(T![Ident]),
279	#[atom(CssAtomSet::InlineStart)]
280	InlineStart(T![Ident]),
281	#[atom(CssAtomSet::BlockEnd)]
282	BlockEnd(T![Ident]),
283	#[atom(CssAtomSet::InlineEnd)]
284	InlineEnd(T![Ident]),
285	#[atom(CssAtomSet::X)]
286	X(T![Ident]),
287	#[atom(CssAtomSet::Y)]
288	Y(T![Ident]),
289	#[atom(CssAtomSet::Block)]
290	Block(T![Ident]),
291	#[atom(CssAtomSet::Inline)]
292	Inline(T![Ident]),
293	#[atom(CssAtomSet::Discrete)]
294	Discrete(T![Ident]),
295}
296
297discrete_feature!(
298	#[derive(Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
299	#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
300	#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
301	pub enum SnappedScrollStateFeature{CssAtomSet::Snapped, SnappedScrollStateFeatureKeyword}
302);
303
304#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
305#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
306#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(skip))]
307pub enum SnappedScrollStateFeatureKeyword {
308	#[atom(CssAtomSet::None)]
309	None(T![Ident]),
310	#[atom(CssAtomSet::X)]
311	X(T![Ident]),
312	#[atom(CssAtomSet::Y)]
313	Y(T![Ident]),
314	#[atom(CssAtomSet::Block)]
315	Block(T![Ident]),
316	#[atom(CssAtomSet::Inline)]
317	Inline(T![Ident]),
318	#[atom(CssAtomSet::Both)]
319	Both(T![Ident]),
320	#[atom(CssAtomSet::Discrete)]
321	Discrete(T![Ident]),
322}
323
324discrete_feature!(
325	#[derive(Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
326	#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
327	#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
328	pub enum StuckScrollStateFeature{CssAtomSet::Stuck, StuckScrollStateFeatureKeyword}
329);
330
331#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
332#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
333#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(skip))]
334pub enum StuckScrollStateFeatureKeyword {
335	#[atom(CssAtomSet::None)]
336	None(T![Ident]),
337	#[atom(CssAtomSet::Top)]
338	Top(T![Ident]),
339	#[atom(CssAtomSet::Right)]
340	Right(T![Ident]),
341	#[atom(CssAtomSet::Bottom)]
342	Bottom(T![Ident]),
343	#[atom(CssAtomSet::Left)]
344	Left(T![Ident]),
345	#[atom(CssAtomSet::BlockStart)]
346	BlockStart(T![Ident]),
347	#[atom(CssAtomSet::InlineStart)]
348	InlineStart(T![Ident]),
349	#[atom(CssAtomSet::BlockEnd)]
350	BlockEnd(T![Ident]),
351	#[atom(CssAtomSet::InlineEnd)]
352	InlineEnd(T![Ident]),
353	#[atom(CssAtomSet::Discrete)]
354	Discrete(T![Ident]),
355}
356
357#[cfg(test)]
358mod tests {
359	use super::*;
360	use crate::CssAtomSet;
361	use css_parse::{assert_parse, assert_parse_error};
362
363	#[test]
364	fn size_test() {
365		assert_eq!(std::mem::size_of::<WidthContainerFeature>(), 124);
366		assert_eq!(std::mem::size_of::<HeightContainerFeature>(), 124);
367		assert_eq!(std::mem::size_of::<InlineSizeContainerFeature>(), 124);
368		assert_eq!(std::mem::size_of::<BlockSizeContainerFeature>(), 124);
369		assert_eq!(std::mem::size_of::<AspectRatioContainerFeature>(), 180);
370		assert_eq!(std::mem::size_of::<OrientationContainerFeature>(), 64);
371		assert_eq!(std::mem::size_of::<StyleQuery>(), 384);
372		assert_eq!(std::mem::size_of::<ScrollStateQuery>(), 80);
373		assert_eq!(std::mem::size_of::<ScrollStateFeature>(), 68);
374		assert_eq!(std::mem::size_of::<ScrollableScrollStateFeature>(), 64);
375		assert_eq!(std::mem::size_of::<SnappedScrollStateFeature>(), 64);
376		assert_eq!(std::mem::size_of::<StuckScrollStateFeature>(), 64);
377	}
378
379	#[test]
380	fn test_writes() {
381		assert_parse!(CssAtomSet::ATOMS, WidthContainerFeature, "(width:360px)");
382		assert_parse!(CssAtomSet::ATOMS, WidthContainerFeature, "(width>=1400px)");
383		assert_parse!(CssAtomSet::ATOMS, WidthContainerFeature, "(100px<=width)");
384		assert_parse!(CssAtomSet::ATOMS, WidthContainerFeature, "(100px<=width>1400px)");
385		assert_parse!(CssAtomSet::ATOMS, HeightContainerFeature, "(height:360px)");
386		assert_parse!(CssAtomSet::ATOMS, HeightContainerFeature, "(height>=1400px)");
387		assert_parse!(CssAtomSet::ATOMS, HeightContainerFeature, "(100px<=height)");
388		assert_parse!(CssAtomSet::ATOMS, HeightContainerFeature, "(100px<=height>1400px)");
389	}
390
391	#[test]
392	fn test_errors() {
393		assert_parse_error!(CssAtomSet::ATOMS, WidthContainerFeature, "(min-width > 10px)");
394		assert_parse_error!(CssAtomSet::ATOMS, WidthContainerFeature, "(width: 1%)");
395	}
396}