1use crate::CssDiagnostic;
2#[cfg(feature = "visitable")]
3use crate::visit::{Visit, VisitMut, Visitable, VisitableMut};
4use css_parse::{Cursor, Diagnostic, Parse, Parser, Peek, Result, SemanticEq, ToCursors, ToNumberValue, ToSpan};
5use csskit_derives::{NodeWithMetadata, ToCursors as DeriveToCursors, ToSpan as DeriveToSpan};
6
7#[derive(DeriveToCursors, DeriveToSpan, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(transparent))]
12#[derive(NodeWithMetadata)]
13pub struct NonNegative<T>(pub T);
14
15impl<T: Into<Cursor>> From<NonNegative<T>> for Cursor {
16 fn from(value: NonNegative<T>) -> Self {
17 value.0.into()
18 }
19}
20
21#[cfg(feature = "visitable")]
22impl<T: Visitable> Visitable for NonNegative<T> {
23 fn accept<V: Visit>(&self, visitor: &mut V) {
24 self.0.accept(visitor);
25 }
26}
27
28#[cfg(feature = "visitable")]
29impl<T: VisitableMut> VisitableMut for NonNegative<T> {
30 fn accept_mut<V: VisitMut>(&mut self, visitor: &mut V) {
31 self.0.accept_mut(visitor);
32 }
33}
34
35impl<'a, T: Peek<'a>> Peek<'a> for NonNegative<T> {
36 fn peek<I>(p: &Parser<'a, I>, c: Cursor) -> bool
37 where
38 I: Iterator<Item = Cursor> + Clone,
39 {
40 T::peek(p, c)
41 }
42}
43
44impl<'a, T: Parse<'a> + ToNumberValue> Parse<'a> for NonNegative<T> {
45 fn parse<I>(p: &mut Parser<'a, I>) -> Result<Self>
46 where
47 I: Iterator<Item = Cursor> + Clone,
48 {
49 let cursor = p.peek_n(1);
50 let value = p.parse::<T>()?;
51 if let Some(num) = value.to_number_value()
52 && num < 0.0
53 {
54 Err(Diagnostic::new(cursor, Diagnostic::non_negative))?;
55 }
56
57 Ok(Self(value))
58 }
59}
60
61impl<T: SemanticEq> SemanticEq for NonNegative<T> {
62 fn semantic_eq(&self, other: &Self) -> bool {
63 self.0.semantic_eq(&other.0)
64 }
65}
66
67impl<T> NonNegative<T> {
68 pub fn inner(&self) -> &T {
70 &self.0
71 }
72
73 pub fn into_inner(self) -> T {
75 self.0
76 }
77}
78
79#[derive(DeriveToCursors, DeriveToSpan, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
83#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(transparent))]
84#[derive(NodeWithMetadata)]
85pub struct Positive<T>(pub T);
86
87impl<T: Into<Cursor>> From<Positive<T>> for Cursor {
88 fn from(value: Positive<T>) -> Self {
89 value.0.into()
90 }
91}
92
93#[cfg(feature = "visitable")]
94impl<T: Visitable> Visitable for Positive<T> {
95 fn accept<V: Visit>(&self, visitor: &mut V) {
96 self.0.accept(visitor);
97 }
98}
99
100#[cfg(feature = "visitable")]
101impl<T: VisitableMut> VisitableMut for Positive<T> {
102 fn accept_mut<V: VisitMut>(&mut self, visitor: &mut V) {
103 self.0.accept_mut(visitor);
104 }
105}
106
107impl<'a, T: Peek<'a>> Peek<'a> for Positive<T> {
108 fn peek<I>(p: &Parser<'a, I>, c: Cursor) -> bool
109 where
110 I: Iterator<Item = Cursor> + Clone,
111 {
112 T::peek(p, c)
113 }
114}
115
116impl<'a, T: Parse<'a> + ToNumberValue> Parse<'a> for Positive<T> {
117 fn parse<I>(p: &mut Parser<'a, I>) -> Result<Self>
118 where
119 I: Iterator<Item = Cursor> + Clone,
120 {
121 let cursor = p.peek_n(1);
122 let value = p.parse::<T>()?;
123
124 if let Some(num) = value.to_number_value()
125 && num <= 0.0
126 {
127 Err(Diagnostic::new(cursor, Diagnostic::positive))?;
128 }
129
130 Ok(Self(value))
131 }
132}
133
134impl<T: SemanticEq> SemanticEq for Positive<T> {
135 fn semantic_eq(&self, other: &Self) -> bool {
136 self.0.semantic_eq(&other.0)
137 }
138}
139
140impl<T> Positive<T> {
141 pub fn inner(&self) -> &T {
143 &self.0
144 }
145
146 pub fn into_inner(self) -> T {
148 self.0
149 }
150}
151
152#[derive(DeriveToCursors, DeriveToSpan, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
156#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(transparent))]
157#[derive(NodeWithMetadata)]
158pub struct NonZero<T>(pub T);
159
160impl<T: Into<Cursor>> From<NonZero<T>> for Cursor {
161 fn from(value: NonZero<T>) -> Self {
162 value.0.into()
163 }
164}
165
166#[cfg(feature = "visitable")]
167impl<T: Visitable> Visitable for NonZero<T> {
168 fn accept<V: Visit>(&self, visitor: &mut V) {
169 self.0.accept(visitor);
170 }
171}
172
173#[cfg(feature = "visitable")]
174impl<T: VisitableMut> VisitableMut for NonZero<T> {
175 fn accept_mut<V: VisitMut>(&mut self, visitor: &mut V) {
176 self.0.accept_mut(visitor);
177 }
178}
179
180impl<'a, T: Peek<'a>> Peek<'a> for NonZero<T> {
181 fn peek<I>(p: &Parser<'a, I>, c: Cursor) -> bool
182 where
183 I: Iterator<Item = Cursor> + Clone,
184 {
185 T::peek(p, c)
186 }
187}
188
189impl<'a, T: Parse<'a> + ToNumberValue> Parse<'a> for NonZero<T> {
190 fn parse<I>(p: &mut Parser<'a, I>) -> Result<Self>
191 where
192 I: Iterator<Item = Cursor> + Clone,
193 {
194 let cursor = p.peek_n(1);
195 let value = p.parse::<T>()?;
196
197 if let Some(num) = value.to_number_value()
198 && num == 0.0
199 {
200 Err(Diagnostic::new(cursor, <Diagnostic as CssDiagnostic>::unexpected_zero))?;
201 }
202
203 Ok(Self(value))
204 }
205}
206
207impl<T: SemanticEq> SemanticEq for NonZero<T> {
208 fn semantic_eq(&self, other: &Self) -> bool {
209 self.0.semantic_eq(&other.0)
210 }
211}
212
213impl<T> NonZero<T> {
214 pub fn inner(&self) -> &T {
216 &self.0
217 }
218
219 pub fn into_inner(self) -> T {
221 self.0
222 }
223}
224
225#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
229#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(transparent))]
230#[derive(NodeWithMetadata)]
231pub struct Ranged<T, const MIN: i32, const MAX: i32>(pub T);
232
233impl<T: Into<Cursor>, const MIN: i32, const MAX: i32> From<Ranged<T, MIN, MAX>> for Cursor {
234 fn from(value: Ranged<T, MIN, MAX>) -> Self {
235 value.0.into()
236 }
237}
238
239impl<T: ToCursors, const MIN: i32, const MAX: i32> ToCursors for Ranged<T, MIN, MAX> {
240 fn to_cursors(&self, s: &mut impl css_parse::CursorSink) {
241 self.0.to_cursors(s);
242 }
243}
244
245impl<T: ToSpan, const MIN: i32, const MAX: i32> ToSpan for Ranged<T, MIN, MAX> {
246 fn to_span(&self) -> css_lexer::Span {
247 self.0.to_span()
248 }
249}
250
251#[cfg(feature = "visitable")]
252impl<T: Visitable, const MIN: i32, const MAX: i32> Visitable for Ranged<T, MIN, MAX> {
253 fn accept<V: Visit>(&self, visitor: &mut V) {
254 self.0.accept(visitor);
255 }
256}
257
258#[cfg(feature = "visitable")]
259impl<T: VisitableMut, const MIN: i32, const MAX: i32> VisitableMut for Ranged<T, MIN, MAX> {
260 fn accept_mut<V: VisitMut>(&mut self, visitor: &mut V) {
261 self.0.accept_mut(visitor);
262 }
263}
264
265impl<'a, T: Peek<'a>, const MIN: i32, const MAX: i32> Peek<'a> for Ranged<T, MIN, MAX> {
266 fn peek<I>(p: &Parser<'a, I>, c: Cursor) -> bool
267 where
268 I: Iterator<Item = Cursor> + Clone,
269 {
270 if !T::peek(p, c) {
271 return false;
272 }
273 let kind = c.token().kind();
274 if kind == css_lexer::Kind::Number || kind == css_lexer::Kind::Dimension {
275 let num = c.token().value();
276 num >= MIN as f32 && num <= MAX as f32
277 } else {
278 true
279 }
280 }
281}
282
283impl<'a, T: Parse<'a> + ToNumberValue, const MIN: i32, const MAX: i32> Parse<'a> for Ranged<T, MIN, MAX> {
284 fn parse<I>(p: &mut Parser<'a, I>) -> Result<Self>
285 where
286 I: Iterator<Item = Cursor> + Clone,
287 {
288 let cursor = p.peek_n(1);
289 let value = p.parse::<T>()?;
290
291 if let Some(num) = value.to_number_value()
292 && (num < MIN as f32 || num > MAX as f32)
293 {
294 Err(Diagnostic::new(cursor, Diagnostic::number_out_of_bounds))?;
295 }
296
297 Ok(Self(value))
298 }
299}
300
301impl<T: SemanticEq, const MIN: i32, const MAX: i32> SemanticEq for Ranged<T, MIN, MAX> {
302 fn semantic_eq(&self, other: &Self) -> bool {
303 self.0.semantic_eq(&other.0)
304 }
305}
306
307impl<T: ToNumberValue, const MIN: i32, const MAX: i32> ToNumberValue for Ranged<T, MIN, MAX> {
308 fn to_number_value(&self) -> Option<f32> {
309 self.0.to_number_value()
310 }
311}
312
313impl<T, const MIN: i32, const MAX: i32> Ranged<T, MIN, MAX> {
314 pub fn inner(&self) -> &T {
316 &self.0
317 }
318
319 pub fn into_inner(self) -> T {
321 self.0
322 }
323}
324
325#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
329#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(transparent))]
330#[derive(NodeWithMetadata)]
331pub struct Exact<T, const VALUE: i32>(pub T);
332
333impl<T: Into<Cursor>, const VALUE: i32> From<Exact<T, VALUE>> for Cursor {
334 fn from(value: Exact<T, VALUE>) -> Self {
335 value.0.into()
336 }
337}
338
339impl<T: ToCursors, const VALUE: i32> ToCursors for Exact<T, VALUE> {
340 fn to_cursors(&self, s: &mut impl css_parse::CursorSink) {
341 self.0.to_cursors(s);
342 }
343}
344
345impl<T: ToSpan, const VALUE: i32> ToSpan for Exact<T, VALUE> {
346 fn to_span(&self) -> css_lexer::Span {
347 self.0.to_span()
348 }
349}
350
351#[cfg(feature = "visitable")]
352impl<T: Visitable, const VALUE: i32> Visitable for Exact<T, VALUE> {
353 fn accept<V: Visit>(&self, visitor: &mut V) {
354 self.0.accept(visitor);
355 }
356}
357
358#[cfg(feature = "visitable")]
359impl<T: VisitableMut, const VALUE: i32> VisitableMut for Exact<T, VALUE> {
360 fn accept_mut<V: VisitMut>(&mut self, visitor: &mut V) {
361 self.0.accept_mut(visitor);
362 }
363}
364
365impl<'a, T: Peek<'a>, const VALUE: i32> Peek<'a> for Exact<T, VALUE> {
366 fn peek<I>(p: &Parser<'a, I>, c: Cursor) -> bool
367 where
368 I: Iterator<Item = Cursor> + Clone,
369 {
370 if !T::peek(p, c) {
371 return false;
372 }
373 let kind = c.token().kind();
374 if kind == css_lexer::Kind::Number || kind == css_lexer::Kind::Dimension {
375 c.token().value() == VALUE as f32
376 } else {
377 true
378 }
379 }
380}
381
382impl<'a, T: Parse<'a> + ToNumberValue, const VALUE: i32> Parse<'a> for Exact<T, VALUE> {
383 fn parse<I>(p: &mut Parser<'a, I>) -> Result<Self>
384 where
385 I: Iterator<Item = Cursor> + Clone,
386 {
387 let cursor = p.peek_n(1);
388 let value = p.parse::<T>()?;
389
390 if let Some(num) = value.to_number_value()
391 && num != VALUE as f32
392 {
393 Err(Diagnostic::new(cursor, Diagnostic::number_out_of_bounds))?;
394 }
395
396 Ok(Self(value))
397 }
398}
399
400impl<T: SemanticEq, const VALUE: i32> SemanticEq for Exact<T, VALUE> {
401 fn semantic_eq(&self, other: &Self) -> bool {
402 self.0.semantic_eq(&other.0)
403 }
404}
405
406impl<T: ToNumberValue, const VALUE: i32> ToNumberValue for Exact<T, VALUE> {
407 fn to_number_value(&self) -> Option<f32> {
408 self.0.to_number_value()
409 }
410}
411
412impl<T, const VALUE: i32> Exact<T, VALUE> {
413 pub fn inner(&self) -> &T {
415 &self.0
416 }
417
418 pub fn into_inner(self) -> T {
420 self.0
421 }
422}
423
424#[cfg(test)]
425mod tests {
426 use super::*;
427 use crate::CssAtomSet;
428 use css_parse::{T, assert_parse, assert_parse_error};
429
430 type ExactOne = Exact<T![Number], 1>;
431 type RangedZeroOne = Ranged<T![Number], 0, 1>;
432
433 #[test]
434 fn test_exact_accepts_correct_value() {
435 assert_parse!(CssAtomSet::ATOMS, ExactOne, "1");
436 }
437
438 #[test]
439 fn test_exact_rejects_wrong_value() {
440 assert_parse_error!(CssAtomSet::ATOMS, ExactOne, "2");
441 }
442
443 #[test]
444 fn test_ranged_accepts_within_range() {
445 assert_parse!(CssAtomSet::ATOMS, RangedZeroOne, "0.5");
446 }
447
448 #[test]
449 fn test_ranged_rejects_out_of_range() {
450 assert_parse_error!(CssAtomSet::ATOMS, RangedZeroOne, "1.5");
451 }
452
453 #[test]
454 fn test_non_negative_accepts_zero() {
455 assert_parse!(CssAtomSet::ATOMS, NonNegative<T![Number]>, "0");
456 }
457
458 #[test]
459 fn test_non_negative_rejects_negative() {
460 assert_parse_error!(CssAtomSet::ATOMS, NonNegative<T![Number]>, "-1");
461 }
462
463 #[test]
464 fn test_positive_accepts_positive() {
465 assert_parse!(CssAtomSet::ATOMS, Positive<T![Number]>, "1");
466 }
467
468 #[test]
469 fn test_positive_rejects_zero() {
470 assert_parse_error!(CssAtomSet::ATOMS, Positive<T![Number]>, "0");
471 }
472
473 #[test]
474 fn test_non_zero_accepts_positive() {
475 assert_parse!(CssAtomSet::ATOMS, NonZero<T![Number]>, "1");
476 }
477
478 #[test]
479 fn test_non_zero_accepts_negative() {
480 assert_parse!(CssAtomSet::ATOMS, NonZero<T![Number]>, "-1");
481 }
482
483 #[test]
484 fn test_non_zero_rejects_zero() {
485 assert_parse_error!(CssAtomSet::ATOMS, NonZero<T![Number]>, "0");
486 }
487}