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