1use super::prelude::*;
2
3use crate::CSSInt;
4use css_parse::{CursorSink, Nth as NthTrait, SemanticEq, Span, ToCursors, ToSpan};
5
6#[node]
7#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
9#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
10#[derive(csskit_derives::NodeWithMetadata)]
11pub enum Nth {
12 Odd(T![Ident]),
13 Even(T![Ident]),
14 Integer(CSSInt),
15 Anb(#[metadata(skip)] i32, #[metadata(skip)] i32, #[metadata(skip)] [Cursor; 4]),
16}
17
18impl<'a> Peek<'a> for Nth {
19 const PEEK_KINDSET: KindSet = <Self as NthTrait<'a>>::NTH_KINDSET;
20}
21
22impl<'a> NthTrait<'a> for Nth {
23 fn peek_odd<I>(p: &Parser<'a, I>, c: Cursor) -> bool
24 where
25 I: Iterator<Item = Cursor> + Clone,
26 {
27 p.to_atom::<CssAtomSet>(c) == CssAtomSet::Odd
28 }
29
30 fn peek_even<I>(p: &Parser<'a, I>, c: Cursor) -> bool
31 where
32 I: Iterator<Item = Cursor> + Clone,
33 {
34 p.to_atom::<CssAtomSet>(c) == CssAtomSet::Even
35 }
36
37 fn build_odd(node: T![Ident]) -> Self {
38 Self::Odd(node)
39 }
40
41 fn build_even(node: T![Ident]) -> Self {
42 Self::Even(node)
43 }
44
45 fn build_integer(node: T![Number]) -> Self {
46 Self::Integer(CSSInt(node))
47 }
48
49 fn build_anb(a: i32, b: i32, cursors: [Cursor; 4]) -> Self {
50 Self::Anb(a, b, cursors)
51 }
52}
53
54impl<'a> Parse<'a> for Nth {
55 fn parse<I>(p: &mut Parser<'a, I>) -> ParserResult<Self>
56 where
57 I: Iterator<Item = Cursor> + Clone,
58 {
59 Self::parse_nth(p)
60 }
61}
62
63impl ToCursors for Nth {
64 fn to_cursors(&self, s: &mut impl CursorSink) {
65 match self {
66 Self::Odd(c) => ToCursors::to_cursors(c, s),
67 Self::Even(c) => ToCursors::to_cursors(c, s),
68 Self::Integer(c) => ToCursors::to_cursors(c, s),
69 Self::Anb(_, _, cursors) => {
70 for c in cursors {
71 if *c != Cursor::EMPTY {
72 s.append(*c);
73 }
74 }
75 }
76 }
77 }
78}
79
80impl Nth {
81 pub fn matches(&self, index: i32) -> bool {
90 match self {
91 Self::Odd(_) => index % 2 == 1,
92 Self::Even(_) => index % 2 == 0,
93 Self::Integer(n) => index == i32::from(*n),
94 Self::Anb(a, b, _) => Self::anb_matches(*a, *b, index),
95 }
96 }
97}
98
99impl SemanticEq for Nth {
100 fn semantic_eq(&self, other: &Self, source_text: &str) -> bool {
101 match (self, other) {
102 (Self::Odd(a), Self::Odd(b)) => a.semantic_eq(b, source_text),
103 (Self::Even(a), Self::Even(b)) => a.semantic_eq(b, source_text),
104 (Self::Integer(a), Self::Integer(b)) => a.semantic_eq(b, source_text),
105 (Self::Anb(a1, b1, _), Self::Anb(a2, b2, _)) => a1 == a2 && b1 == b2,
106 _ => false,
107 }
108 }
109}
110
111impl ToSpan for Nth {
112 fn to_span(&self) -> Span {
113 match self {
114 Nth::Odd(c) => c.to_span(),
115 Nth::Even(c) => c.to_span(),
116 Nth::Integer(c) => c.to_span(),
117 Nth::Anb(_, _, cursors) => {
118 let mut span = Span::DUMMY;
119 for c in cursors {
120 if *c != Cursor::EMPTY {
121 span = span + (*c).into()
122 }
123 }
124 span
125 }
126 }
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133 use crate::CssAtomSet;
134 use css_parse::{assert_parse, assert_parse_error, assert_parse_span};
135
136 #[test]
137 fn test_writes() {
138 assert_parse!(CssAtomSet::ATOMS, Nth, "odd");
139 assert_parse!(CssAtomSet::ATOMS, Nth, "ODD");
140 assert_parse!(CssAtomSet::ATOMS, Nth, "eVeN");
141 assert_parse!(CssAtomSet::ATOMS, Nth, "5");
142 assert_parse!(CssAtomSet::ATOMS, Nth, "n");
143 assert_parse!(CssAtomSet::ATOMS, Nth, "+n");
144 assert_parse!(CssAtomSet::ATOMS, Nth, "+N");
145 assert_parse!(CssAtomSet::ATOMS, Nth, "-n");
146 assert_parse!(CssAtomSet::ATOMS, Nth, "+5");
147 assert_parse!(CssAtomSet::ATOMS, Nth, "5n");
148 assert_parse!(CssAtomSet::ATOMS, Nth, "+5n");
149 assert_parse!(CssAtomSet::ATOMS, Nth, "-5n");
150 assert_parse!(CssAtomSet::ATOMS, Nth, "n-4");
151 assert_parse!(CssAtomSet::ATOMS, Nth, "-n-4");
152 assert_parse!(CssAtomSet::ATOMS, Nth, "+n-4");
153 assert_parse!(CssAtomSet::ATOMS, Nth, "+n+4");
154 assert_parse!(CssAtomSet::ATOMS, Nth, "+n-123456789");
155 assert_parse!(CssAtomSet::ATOMS, Nth, "2n");
156 assert_parse!(CssAtomSet::ATOMS, Nth, "2n+1");
157 assert_parse!(CssAtomSet::ATOMS, Nth, "+2n+1");
158 assert_parse!(CssAtomSet::ATOMS, Nth, "-2n+1");
159 assert_parse!(CssAtomSet::ATOMS, Nth, "-2n-1");
160 assert_parse!(CssAtomSet::ATOMS, Nth, "+2n-1");
161 assert_parse!(CssAtomSet::ATOMS, Nth, "3n+4");
162 assert_parse!(CssAtomSet::ATOMS, Nth, "3n+1");
163 assert_parse!(CssAtomSet::ATOMS, Nth, "n+ 3");
164 assert_parse!(CssAtomSet::ATOMS, Nth, "-n+3");
165
166 assert_parse!(CssAtomSet::ATOMS, Nth, "1n+0");
168 assert_parse!(CssAtomSet::ATOMS, Nth, "n+0");
169 assert_parse!(CssAtomSet::ATOMS, Nth, "n");
170 assert_parse!(CssAtomSet::ATOMS, Nth, "-n+0");
171 assert_parse!(CssAtomSet::ATOMS, Nth, "-n");
172 assert_parse!(CssAtomSet::ATOMS, Nth, "N");
173 assert_parse!(CssAtomSet::ATOMS, Nth, "+n+3");
174 assert_parse!(CssAtomSet::ATOMS, Nth, "+n + 7 ");
175 assert_parse!(CssAtomSet::ATOMS, Nth, "N- 123");
176 assert_parse!(CssAtomSet::ATOMS, Nth, "n- 10");
177 assert_parse!(CssAtomSet::ATOMS, Nth, "-n\n- 1");
178 assert_parse!(CssAtomSet::ATOMS, Nth, " 23n\n\n+\n\n123 ");
179 assert_parse!(CssAtomSet::ATOMS, Nth, "-n- 4");
181 assert_parse!(CssAtomSet::ATOMS, Nth, "5n- 4");
182 assert_parse!(CssAtomSet::ATOMS, Nth, "-5n- 4");
183 }
184
185 #[test]
186 fn test_spans() {
187 assert_parse_span!(
188 CssAtomSet::ATOMS,
189 Nth,
190 r#"
191 odd
192 ^^^
193 "#
194 );
195 assert_parse_span!(
196 CssAtomSet::ATOMS,
197 Nth,
198 r#"
199 5
200 ^
201 "#
202 );
203 assert_parse_span!(
204 CssAtomSet::ATOMS,
205 Nth,
206 r#"
207 2n
208 ^^
209 "#
210 );
211 assert_parse_span!(
212 CssAtomSet::ATOMS,
213 Nth,
214 r#"
215 -2n+1 foo
216 ^^^^^
217 "#
218 );
219 assert_parse_span!(
220 CssAtomSet::ATOMS,
221 Nth,
222 r#"
223 n- 10
224 ^^^^^
225 "#
226 );
227 }
228
229 #[test]
230 fn test_errors() {
231 assert_parse_error!(CssAtomSet::ATOMS, Nth, "3n + -6");
232 assert_parse_error!(CssAtomSet::ATOMS, Nth, "3 n");
233 assert_parse_error!(CssAtomSet::ATOMS, Nth, "+ 2n");
234 assert_parse_error!(CssAtomSet::ATOMS, Nth, "+ 2");
235
236 assert_parse_error!(CssAtomSet::ATOMS, Nth, "n- 1 2");
238 assert_parse_error!(CssAtomSet::ATOMS, Nth, "n-b1");
239 assert_parse_error!(CssAtomSet::ATOMS, Nth, "n-+1");
240 assert_parse_error!(CssAtomSet::ATOMS, Nth, "n-1n");
241 assert_parse_error!(CssAtomSet::ATOMS, Nth, "-n -b1");
242 assert_parse_error!(CssAtomSet::ATOMS, Nth, "-1n- b1");
243 assert_parse_error!(CssAtomSet::ATOMS, Nth, "-n-13b1");
244 assert_parse_error!(CssAtomSet::ATOMS, Nth, "-n-+1");
245 assert_parse_error!(CssAtomSet::ATOMS, Nth, "-n+n");
246 assert_parse_error!(CssAtomSet::ATOMS, Nth, "+ 1n");
247 assert_parse_error!(CssAtomSet::ATOMS, Nth, " n +12 3");
248 assert_parse_error!(CssAtomSet::ATOMS, Nth, " 12 n ");
249 assert_parse_error!(CssAtomSet::ATOMS, Nth, "+12n-0+1");
250 assert_parse_error!(CssAtomSet::ATOMS, Nth, "+12N -- 1");
251 assert_parse_error!(CssAtomSet::ATOMS, Nth, "+12 N ");
252 assert_parse_error!(CssAtomSet::ATOMS, Nth, "+ n + 7");
253 }
254
255 }