1use swc_atoms::Atom;
2use swc_common::{BytePos, Span};
3use swc_css_ast::*;
4
5use super::{input::ParserInput, PResult, Parser};
6use crate::{
7 error::{Error, ErrorKind},
8 Parse,
9};
10
11impl<I> Parser<I>
12where
13 I: ParserInput,
14{
15 pub(super) fn parse_generic_values(&mut self) -> PResult<Vec<ComponentValue>> {
16 let mut values = Vec::new();
17
18 loop {
19 self.input.skip_ws();
20
21 if is!(self, EOF) {
22 break;
23 }
24
25 let component_value = self.parse_generic_value()?;
26
27 values.push(component_value);
28 }
29
30 Ok(values)
31 }
32
33 pub(super) fn parse_generic_value(&mut self) -> PResult<ComponentValue> {
34 self.input.skip_ws();
35
36 let span = self.input.cur_span();
37
38 match cur!(self) {
39 tok!(",") | tok!("/") | tok!(";") => {
40 return Ok(ComponentValue::Delimiter(self.parse()?));
41 }
42
43 tok!("string") => {
44 return Ok(ComponentValue::Str(self.parse()?));
45 }
46
47 tok!("url") => {
48 return Ok(ComponentValue::Url(self.parse()?));
49 }
50
51 Token::Function { value, .. } => match &*value.to_ascii_lowercase() {
52 "url" | "src" => {
53 return Ok(ComponentValue::Url(self.parse()?));
54 }
55 "rgb" | "rgba" | "hsl" | "hsla" | "hwb" | "lab" | "lch" | "oklab" | "oklch"
56 | "color" | "device-cmyk" | "color-mix" | "color-contrast" => {
57 return Ok(ComponentValue::Color(self.parse()?));
58 }
59 _ => {
60 return Ok(ComponentValue::Function(self.parse()?));
61 }
62 },
63
64 tok!("percentage") => {
65 return Ok(ComponentValue::Percentage(self.parse()?));
66 }
67
68 tok!("dimension") => return Ok(ComponentValue::Dimension(self.parse()?)),
69
70 Token::Number { type_flag, .. } => {
71 if *type_flag == NumberType::Integer {
72 return Ok(ComponentValue::Integer(self.parse()?));
73 }
74
75 return Ok(ComponentValue::Number(self.parse()?));
76 }
77
78 Token::Ident { value, .. } => {
79 if value.starts_with("--") {
80 return Ok(ComponentValue::DashedIdent(self.parse()?));
81 } else if matches_eq_ignore_ascii_case!(value, "u")
82 && peeked_is_one_of!(self, "+", "number", "dimension")
83 {
84 return Ok(ComponentValue::UnicodeRange(self.parse()?));
85 }
86
87 return Ok(ComponentValue::Ident(self.parse()?));
88 }
89
90 tok!("[") | tok!("(") | tok!("{") => {
91 let mut block = self.parse_as::<SimpleBlock>()?;
92 let locv = self.create_locv(block.value);
93
94 block.value = match self
95 .parse_according_to_grammar(&locv, |parser| parser.parse_generic_values())
96 {
97 Ok(values) => values,
98 Err(err) => {
99 if *err.kind() != ErrorKind::Ignore {
100 self.errors.push(err);
101 }
102
103 locv.children
104 }
105 };
106
107 return Ok(ComponentValue::SimpleBlock(Box::new(block)));
108 }
109
110 tok!("#") => {
111 return Ok(ComponentValue::Color(self.parse()?));
112 }
113
114 _ => {}
115 }
116
117 Err(Error::new(span, ErrorKind::Expected("Declaration value")))
118 }
119
120 pub(super) fn parse_any_value(&mut self) -> PResult<Vec<TokenAndSpan>> {
122 let mut tokens = Vec::new();
123 let mut balance_stack: Vec<Option<char>> = Vec::new();
124
125 loop {
128 if is!(self, EOF) {
129 break;
130 }
131
132 match cur!(self) {
133 tok!("bad-string") | tok!("bad-url") => {
135 break;
136 }
137
138 tok!(")") | tok!("]") | tok!("}") => {
140 let value = match cur!(self) {
141 tok!(")") => ')',
142 tok!("]") => ']',
143 tok!("}") => '}',
144 _ => {
145 unreachable!();
146 }
147 };
148
149 let balance_close_type = balance_stack.pop().unwrap_or_default();
150
151 if Some(value) != balance_close_type {
152 break;
153 }
154 }
155
156 tok!("function") | tok!("(") | tok!("[") | tok!("{") => {
157 let value = match cur!(self) {
158 tok!("function") | tok!("(") => ')',
159 tok!("[") => ']',
160 tok!("{") => '}',
161 _ => {
162 unreachable!();
163 }
164 };
165
166 balance_stack.push(Some(value));
167 }
168
169 _ => {}
170 }
171
172 let token = self.input.bump();
173
174 match token {
175 Some(token) => tokens.push(token),
176 None => break,
177 }
178 }
179
180 Ok(tokens)
181 }
182
183 pub fn parse_function_values(
185 &mut self,
186 function_name: &FunctionName,
187 ) -> PResult<Vec<ComponentValue>> {
188 let function_name = match function_name {
189 FunctionName::Ident(name) => &name.value,
190 FunctionName::DashedIdent(_) => {
191 return Err(Error::new(Default::default(), ErrorKind::Ignore))
192 }
193 };
194
195 let mut values = Vec::new();
196
197 let lower_fname = function_name.to_ascii_lowercase();
198
199 match &*lower_fname {
200 "calc" | "-moz-calc" | "-webkit-calc" | "sin" | "cos" | "tan" | "asin" | "acos"
201 | "atan" | "sqrt" | "exp" | "abs" | "sign" => {
202 self.input.skip_ws();
203
204 let calc_sum = ComponentValue::CalcSum(self.parse()?);
205
206 values.push(calc_sum);
207
208 self.input.skip_ws();
209
210 if !is!(self, EOF) {
211 let span = self.input.cur_span();
212
213 return Err(Error::new(span, ErrorKind::Unexpected("value")));
214 }
215 }
216 "min" | "max" | "hypot" => {
217 self.input.skip_ws();
218
219 let calc_sum = ComponentValue::CalcSum(self.parse()?);
220
221 values.push(calc_sum);
222
223 loop {
224 self.input.skip_ws();
225
226 if is!(self, ",") {
227 values.push(ComponentValue::Delimiter(self.parse()?));
228
229 self.input.skip_ws();
230 } else {
231 break;
232 }
233
234 let calc_sum = ComponentValue::CalcSum(self.parse()?);
235
236 values.push(calc_sum);
237 }
238
239 if !is!(self, EOF) {
240 let span = self.input.cur_span();
241
242 return Err(Error::new(span, ErrorKind::Unexpected("value")));
243 }
244 }
245 "clamp" => {
246 self.input.skip_ws();
247
248 let calc_sum = ComponentValue::CalcSum(self.parse()?);
249
250 values.push(calc_sum);
251
252 self.input.skip_ws();
253
254 if is!(self, ",") {
255 values.push(ComponentValue::Delimiter(self.parse()?));
256
257 self.input.skip_ws();
258 } else {
259 let span = self.input.cur_span();
260
261 return Err(Error::new(span, ErrorKind::Expected("',' delim token")));
262 }
263
264 let calc_sum = ComponentValue::CalcSum(self.parse()?);
265
266 values.push(calc_sum);
267
268 self.input.skip_ws();
269
270 if is!(self, ",") {
271 values.push(ComponentValue::Delimiter(self.parse()?));
272
273 self.input.skip_ws();
274 } else {
275 let span = self.input.cur_span();
276
277 return Err(Error::new(span, ErrorKind::Expected("',' delim token")));
278 }
279
280 let calc_sum = ComponentValue::CalcSum(self.parse()?);
281
282 values.push(calc_sum);
283
284 self.input.skip_ws();
285 }
286 "round" => {
287 self.input.skip_ws();
288
289 if is!(self, "ident") {
290 let rounding_strategy = ComponentValue::Ident(self.parse()?);
292
293 values.push(rounding_strategy);
294
295 self.input.skip_ws();
296
297 if is!(self, ",") {
298 values.push(ComponentValue::Delimiter(self.parse()?));
299
300 self.input.skip_ws();
301 } else {
302 let span = self.input.cur_span();
303
304 return Err(Error::new(span, ErrorKind::Expected("',' delim token")));
305 }
306 }
307
308 let calc_sum = ComponentValue::CalcSum(self.parse()?);
309
310 values.push(calc_sum);
311
312 self.input.skip_ws();
313
314 if is!(self, ",") {
315 values.push(ComponentValue::Delimiter(self.parse()?));
316
317 self.input.skip_ws();
318 } else {
319 let span = self.input.cur_span();
320
321 return Err(Error::new(span, ErrorKind::Expected("',' delim token")));
322 }
323
324 let calc_sum = ComponentValue::CalcSum(self.parse()?);
325
326 values.push(calc_sum);
327
328 self.input.skip_ws();
329 }
330 "mod" | "rem" | "atan2" | "pow" => {
331 self.input.skip_ws();
332
333 let calc_sum = ComponentValue::CalcSum(self.parse()?);
334
335 values.push(calc_sum);
336
337 self.input.skip_ws();
338
339 if is!(self, ",") {
340 values.push(ComponentValue::Delimiter(self.parse()?));
341
342 self.input.skip_ws();
343 } else {
344 let span = self.input.cur_span();
345
346 return Err(Error::new(span, ErrorKind::Expected("',' delim token")));
347 }
348
349 let calc_sum = ComponentValue::CalcSum(self.parse()?);
350
351 values.push(calc_sum);
352
353 self.input.skip_ws();
354 }
355 "log" => {
356 self.input.skip_ws();
357
358 let calc_sum = ComponentValue::CalcSum(self.parse()?);
359
360 values.push(calc_sum);
361
362 self.input.skip_ws();
363
364 if is!(self, ",") {
365 values.push(ComponentValue::Delimiter(self.parse()?));
366
367 self.input.skip_ws();
368
369 let calc_sum = ComponentValue::CalcSum(self.parse()?);
370
371 values.push(calc_sum);
372
373 self.input.skip_ws();
374 }
375 }
376 "rgb" | "rgba" | "hsl" | "hsla" => {
377 self.input.skip_ws();
378
379 let mut has_variable = false;
380 let mut is_legacy_syntax = true;
381
382 match cur!(self) {
383 Token::Ident { value, .. } if matches_eq_ignore_ascii_case!(value, "from") => {
384 is_legacy_syntax = false;
385
386 values.push(ComponentValue::Ident(self.parse()?));
387
388 self.input.skip_ws();
389
390 let color = self.try_parse_variable_function(
391 |parser, _| Ok(Some(ComponentValue::Color(parser.parse()?))),
392 &mut has_variable,
393 )?;
394
395 if let Some(color) = color {
396 values.push(color);
397 }
398
399 self.input.skip_ws();
400 }
401 _ => {}
402 }
403
404 match &*lower_fname {
405 "rgb" | "rgba" => {
406 let percentage_or_number_or_none = self.try_parse_variable_function(
407 |parser, has_variable_before| match cur!(parser) {
408 tok!("percentage") => {
409 Ok(Some(ComponentValue::Percentage(parser.parse()?)))
410 }
411 tok!("number") => Ok(Some(ComponentValue::Number(parser.parse()?))),
412 Token::Function { value, .. } if is_math_function(value) => {
413 Ok(Some(ComponentValue::Function(parser.parse()?)))
414 }
415 tok!("ident") => {
416 is_legacy_syntax = false;
417
418 let ident: Box<Ident> = parser.parse()?;
419
420 if ident.value.eq_ignore_ascii_case("none") {
421 Ok(Some(ComponentValue::Ident(ident)))
422 } else {
423 Err(Error::new(
424 ident.span,
425 ErrorKind::Expected("'none' value of an ident token"),
426 ))
427 }
428 }
429 _ => {
430 if !has_variable_before {
431 Err(Error::new(
432 parser.input.cur_span(),
433 ErrorKind::Expected(
434 "percentage, number, function (math functions) or \
435 ident (with 'none' value) token",
436 ),
437 ))
438 } else {
439 Ok(None)
440 }
441 }
442 },
443 &mut has_variable,
444 )?;
445
446 if let Some(percentage_or_number_or_none) = percentage_or_number_or_none {
447 values.push(percentage_or_number_or_none);
448 }
449
450 self.input.skip_ws();
451 }
452 "hsl" | "hsla" => {
453 let hue_or_none = self.try_parse_variable_function(
454 |parser, has_variable_before| match cur!(parser) {
455 tok!("number") | tok!("dimension") => {
456 Ok(Some(ComponentValue::Hue(parser.parse()?)))
457 }
458 tok!("ident") => {
459 let ident: Box<Ident> = parser.parse()?;
460
461 if ident.value.eq_ignore_ascii_case("none") {
462 Ok(Some(ComponentValue::Ident(ident)))
463 } else {
464 Err(Error::new(
465 ident.span,
466 ErrorKind::Expected("'none' value of an ident token"),
467 ))
468 }
469 }
470 Token::Function { value, .. } if is_math_function(value) => {
471 Ok(Some(ComponentValue::Function(parser.parse()?)))
472 }
473 _ => {
474 if !has_variable_before {
475 Err(Error::new(
476 parser.input.cur_span(),
477 ErrorKind::Expected(
478 "number, dimension, function (math functions) or \
479 ident (with 'none' value) token",
480 ),
481 ))
482 } else {
483 Ok(None)
484 }
485 }
486 },
487 &mut has_variable,
488 )?;
489
490 if let Some(hue_or_none) = hue_or_none {
491 values.push(hue_or_none);
492 }
493
494 self.input.skip_ws();
495 }
496 _ => {
497 unreachable!()
498 }
499 }
500
501 if is!(self, ",") {
502 if !is_legacy_syntax && !has_variable {
503 let span = self.input.cur_span();
504
505 return Err(Error::new(span, ErrorKind::Expected("comma token")));
506 }
507
508 is_legacy_syntax = true;
509
510 values.push(ComponentValue::Delimiter(self.parse()?));
511
512 self.input.skip_ws();
513 } else {
514 is_legacy_syntax = false;
515 }
516
517 match &*lower_fname {
518 "rgb" | "rgba" => {
519 let percentage_or_number = self.try_parse_variable_function(
520 |parser, has_variable_before| match cur!(parser) {
521 tok!("percentage") => {
522 Ok(Some(ComponentValue::Percentage(parser.parse()?)))
523 }
524 tok!("number") => Ok(Some(ComponentValue::Number(parser.parse()?))),
525 Token::Function { value, .. } if is_math_function(value) => {
526 Ok(Some(ComponentValue::Function(parser.parse()?)))
527 }
528 tok!("ident") if !is_legacy_syntax => {
529 let ident: Box<Ident> = parser.parse()?;
530
531 if ident.value.eq_ignore_ascii_case("none") {
532 Ok(Some(ComponentValue::Ident(ident)))
533 } else {
534 Err(Error::new(
535 ident.span,
536 ErrorKind::Expected("'none' value of an ident token"),
537 ))
538 }
539 }
540 _ => {
541 if !has_variable_before {
542 Err(Error::new(
543 parser.input.cur_span(),
544 ErrorKind::Expected(
545 "percentage, number, function (math functions) or \
546 ident (with 'none' value) token",
547 ),
548 ))
549 } else {
550 Ok(None)
551 }
552 }
553 },
554 &mut has_variable,
555 )?;
556
557 if let Some(percentage_or_number) = percentage_or_number {
558 values.push(percentage_or_number);
559 }
560
561 self.input.skip_ws();
562 }
563 "hsl" | "hsla" => {
564 let percentage_or_none = self.try_parse_variable_function(
565 |parser, has_variable_before| match cur!(parser) {
566 tok!("percentage") => {
567 Ok(Some(ComponentValue::Percentage(parser.parse()?)))
568 }
569 Token::Function { value, .. } if is_math_function(value) => {
570 Ok(Some(ComponentValue::Function(parser.parse()?)))
571 }
572 tok!("ident") => {
573 let ident: Box<Ident> = parser.parse()?;
574
575 if ident.value.eq_ignore_ascii_case("none") {
576 Ok(Some(ComponentValue::Ident(ident)))
577 } else {
578 Err(Error::new(
579 ident.span,
580 ErrorKind::Expected("'none' value of an ident token"),
581 ))
582 }
583 }
584 _ => {
585 if !has_variable_before {
586 Err(Error::new(
587 parser.input.cur_span(),
588 ErrorKind::Expected(
589 "percentage, function (math functions) or ident \
590 (with 'none' value) token",
591 ),
592 ))
593 } else {
594 Ok(None)
595 }
596 }
597 },
598 &mut has_variable,
599 )?;
600
601 if let Some(percentage_or_none) = percentage_or_none {
602 values.push(percentage_or_none);
603 }
604
605 self.input.skip_ws();
606 }
607 _ => {
608 unreachable!();
609 }
610 }
611
612 if is_legacy_syntax {
613 if has_variable && is!(self, EOF) {
614 return Ok(values);
615 }
616
617 match cur!(self) {
618 tok!(",") => {
619 values.push(ComponentValue::Delimiter(self.parse()?));
620
621 self.input.skip_ws();
622 }
623 _ => {
624 if !has_variable {
625 let span = self.input.cur_span();
626
627 return Err(Error::new(span, ErrorKind::Expected("comma token")));
628 }
629 }
630 }
631 }
632
633 match &*lower_fname {
634 "rgb" | "rgba" => {
635 let percentage_or_number = self.try_parse_variable_function(
636 |parser, has_variable_before| match cur!(parser) {
637 tok!("percentage") => {
638 Ok(Some(ComponentValue::Percentage(parser.parse()?)))
639 }
640 tok!("number") => Ok(Some(ComponentValue::Number(parser.parse()?))),
641 Token::Function { value, .. } if is_math_function(value) => {
642 Ok(Some(ComponentValue::Function(parser.parse()?)))
643 }
644 tok!("ident") if !is_legacy_syntax => {
645 let ident: Box<Ident> = parser.parse()?;
646
647 if ident.value.eq_ignore_ascii_case("none") {
648 Ok(Some(ComponentValue::Ident(ident)))
649 } else {
650 Err(Error::new(
651 ident.span,
652 ErrorKind::Expected("'none' value of an ident token"),
653 ))
654 }
655 }
656 _ => {
657 if !has_variable_before {
658 Err(Error::new(
659 parser.input.cur_span(),
660 ErrorKind::Expected(
661 "percentage, number, function (math functions) or \
662 ident (with 'none' value) token",
663 ),
664 ))
665 } else {
666 Ok(None)
667 }
668 }
669 },
670 &mut has_variable,
671 )?;
672
673 if let Some(percentage_or_number) = percentage_or_number {
674 values.push(percentage_or_number);
675 }
676
677 self.input.skip_ws();
678 }
679 "hsl" | "hsla" => {
680 let percentage_or_none = self.try_parse_variable_function(
681 |parser, has_variable_before| match cur!(parser) {
682 tok!("percentage") => {
683 Ok(Some(ComponentValue::Percentage(parser.parse()?)))
684 }
685 Token::Function { value, .. } if is_math_function(value) => {
686 Ok(Some(ComponentValue::Function(parser.parse()?)))
687 }
688 tok!("ident") => {
689 let ident: Box<Ident> = parser.parse()?;
690
691 if ident.value.eq_ignore_ascii_case("none") {
692 Ok(Some(ComponentValue::Ident(ident)))
693 } else {
694 Err(Error::new(
695 ident.span,
696 ErrorKind::Expected("'none' value of an ident token"),
697 ))
698 }
699 }
700 _ => {
701 if !has_variable_before {
702 Err(Error::new(
703 parser.input.cur_span(),
704 ErrorKind::Expected(
705 "percentage, function (math functions) or ident \
706 (with 'none' value) token",
707 ),
708 ))
709 } else {
710 Ok(None)
711 }
712 }
713 },
714 &mut has_variable,
715 )?;
716
717 if let Some(percentage_or_none) = percentage_or_none {
718 values.push(percentage_or_none);
719 }
720
721 self.input.skip_ws();
722 }
723 _ => {
724 unreachable!();
725 }
726 }
727
728 if (is!(self, ",") || has_variable) && is_legacy_syntax {
729 if is!(self, ",") {
730 values.push(ComponentValue::Delimiter(self.parse()?));
731 }
732
733 self.input.skip_ws();
734
735 let alpha_value = self.try_parse_variable_function(
736 |parser, has_variable_before| match cur!(parser) {
737 tok!("number") | tok!("percentage") => {
738 Ok(Some(ComponentValue::AlphaValue(parser.parse()?)))
739 }
740 Token::Function { value, .. } if is_math_function(value) => {
741 Ok(Some(ComponentValue::Function(parser.parse()?)))
742 }
743 _ => {
744 if !has_variable_before {
745 Err(Error::new(
746 parser.input.cur_span(),
747 ErrorKind::Expected(
748 "percentage, function (math functions) or number token",
749 ),
750 ))
751 } else {
752 Ok(None)
753 }
754 }
755 },
756 &mut has_variable,
757 )?;
758
759 if let Some(alpha_value) = alpha_value {
760 values.push(alpha_value);
761 }
762
763 self.input.skip_ws();
764 } else if is!(self, "/") && !is_legacy_syntax {
765 values.push(ComponentValue::Delimiter(self.parse()?));
766
767 self.input.skip_ws();
768
769 let alpha_value = self.try_parse_variable_function(
770 |parser, has_variable_before| match cur!(parser) {
771 tok!("number") | tok!("percentage") => {
772 Ok(Some(ComponentValue::AlphaValue(parser.parse()?)))
773 }
774 Token::Function { value, .. } if is_math_function(value) => {
775 Ok(Some(ComponentValue::Function(parser.parse()?)))
776 }
777 tok!("ident") => {
778 let ident: Box<Ident> = parser.parse()?;
779
780 if ident.value.eq_ignore_ascii_case("none") {
781 Ok(Some(ComponentValue::Ident(ident)))
782 } else {
783 Err(Error::new(
784 ident.span,
785 ErrorKind::Expected("'none' value of an ident token"),
786 ))
787 }
788 }
789 _ => {
790 if !has_variable_before {
791 Err(Error::new(
792 parser.input.cur_span(),
793 ErrorKind::Expected(
794 "percentage, number, function (math functions) or \
795 ident (with 'none' value) token",
796 ),
797 ))
798 } else {
799 Ok(None)
800 }
801 }
802 },
803 &mut has_variable,
804 )?;
805
806 if let Some(alpha_value) = alpha_value {
807 values.push(alpha_value);
808 }
809
810 self.input.skip_ws();
811 }
812 }
813 "hwb" | "lab" | "lch" | "oklab" | "oklch" | "device-cmyk" => {
814 self.input.skip_ws();
815
816 let mut has_variable = false;
817
818 match cur!(self) {
819 Token::Ident { value, .. }
820 if matches_eq_ignore_ascii_case!(value, "from")
821 && lower_fname != "device-cmyk" =>
822 {
823 values.push(ComponentValue::Ident(self.parse()?));
824
825 self.input.skip_ws();
826
827 let color = self.try_parse_variable_function(
828 |parser, _| Ok(Some(ComponentValue::Color(parser.parse()?))),
829 &mut has_variable,
830 )?;
831
832 if let Some(color) = color {
833 values.push(color);
834 }
835
836 self.input.skip_ws();
837 }
838 _ => {}
839 }
840
841 match &*lower_fname {
842 "hwb" => {
843 let hue_or_none = self.try_parse_variable_function(
844 |parser, has_variable_before| match cur!(parser) {
845 tok!("number") | tok!("dimension") => {
846 Ok(Some(ComponentValue::Hue(parser.parse()?)))
847 }
848 Token::Function { value, .. } if is_math_function(value) => {
849 Ok(Some(ComponentValue::Function(parser.parse()?)))
850 }
851 tok!("ident") => {
852 let ident: Box<Ident> = parser.parse()?;
853
854 if ident.value.eq_ignore_ascii_case("none") {
855 Ok(Some(ComponentValue::Ident(ident)))
856 } else {
857 Err(Error::new(
858 ident.span,
859 ErrorKind::Expected("'none' value of an ident token"),
860 ))
861 }
862 }
863 _ => {
864 if !has_variable_before {
865 return Err(Error::new(
866 parser.input.cur_span(),
867 ErrorKind::Expected(
868 "number, dimension, function (math functions) or \
869 ident (with 'none' value) token",
870 ),
871 ));
872 } else {
873 Ok(None)
874 }
875 }
876 },
877 &mut has_variable,
878 )?;
879
880 if let Some(hue_or_none) = hue_or_none {
881 values.push(hue_or_none);
882 }
883
884 self.input.skip_ws();
885 }
886 "lab" | "lch" | "oklab" | "oklch" => {
887 let percentage_or_none = self.try_parse_variable_function(
888 |parser, has_variable_before| match cur!(parser) {
889 tok!("percentage") => {
890 Ok(Some(ComponentValue::Percentage(parser.parse()?)))
891 }
892 tok!("number") => Ok(Some(ComponentValue::Number(parser.parse()?))),
893 Token::Function { value, .. } if is_math_function(value) => {
894 Ok(Some(ComponentValue::Function(parser.parse()?)))
895 }
896 tok!("ident") => {
897 let ident: Box<Ident> = parser.parse()?;
898
899 if ident.value.eq_ignore_ascii_case("none") {
900 Ok(Some(ComponentValue::Ident(ident)))
901 } else {
902 Err(Error::new(
903 ident.span,
904 ErrorKind::Expected("'none' value of an ident token"),
905 ))
906 }
907 }
908 _ => {
909 if !has_variable_before {
910 Err(Error::new(
911 parser.input.cur_span(),
912 ErrorKind::Expected(
913 "percentage, function (math functions) or ident \
914 (with 'none' value) token",
915 ),
916 ))
917 } else {
918 Ok(None)
919 }
920 }
921 },
922 &mut has_variable,
923 )?;
924
925 if let Some(percentage_or_none) = percentage_or_none {
926 values.push(percentage_or_none);
927 }
928
929 self.input.skip_ws();
930 }
931 "device-cmyk" => {
932 let cmyk_component = self.try_parse_variable_function(
933 |parser, _| Ok(Some(ComponentValue::CmykComponent(parser.parse()?))),
934 &mut has_variable,
935 )?;
936
937 if let Some(cmyk_component) = cmyk_component {
938 values.push(cmyk_component);
939 }
940
941 self.input.skip_ws();
942 }
943 _ => {
944 unreachable!();
945 }
946 }
947
948 if !is_one_of!(self, EOF, "/") {
949 match &*lower_fname {
950 "hwb" => {
951 let percentage_or_none = self.try_parse_variable_function(
952 |parser, has_variable_before| match cur!(parser) {
953 tok!("percentage") => {
954 Ok(Some(ComponentValue::Percentage(parser.parse()?)))
955 }
956 Token::Function { value, .. } if is_math_function(value) => {
957 Ok(Some(ComponentValue::Function(parser.parse()?)))
958 }
959 tok!("ident") => {
960 let ident: Box<Ident> = parser.parse()?;
961
962 if ident.value.eq_ignore_ascii_case("none") {
963 Ok(Some(ComponentValue::Ident(ident)))
964 } else {
965 Err(Error::new(
966 ident.span,
967 ErrorKind::Expected(
968 "'none' value of an ident token",
969 ),
970 ))
971 }
972 }
973 _ => {
974 if !has_variable_before {
975 Err(Error::new(
976 parser.input.cur_span(),
977 ErrorKind::Expected(
978 "percentage, functions (math functions) or \
979 ident (with 'none' value) token",
980 ),
981 ))
982 } else {
983 Ok(None)
984 }
985 }
986 },
987 &mut has_variable,
988 )?;
989
990 if let Some(percentage_or_none) = percentage_or_none {
991 values.push(percentage_or_none);
992 }
993
994 self.input.skip_ws();
995 }
996 "lab" | "lch" | "oklab" | "oklch" => {
997 let number_or_none = self.try_parse_variable_function(
998 |parser, has_variable_before| match cur!(parser) {
999 tok!("percentage") => {
1000 Ok(Some(ComponentValue::Percentage(parser.parse()?)))
1001 }
1002 tok!("number") => {
1003 Ok(Some(ComponentValue::Number(parser.parse()?)))
1004 }
1005 Token::Function { value, .. } if is_math_function(value) => {
1006 Ok(Some(ComponentValue::Function(parser.parse()?)))
1007 }
1008 tok!("ident") => {
1009 let ident: Box<Ident> = parser.parse()?;
1010
1011 if ident.value.eq_ignore_ascii_case("none") {
1012 Ok(Some(ComponentValue::Ident(ident)))
1013 } else {
1014 Err(Error::new(
1015 ident.span,
1016 ErrorKind::Expected(
1017 "'none' value of an ident token",
1018 ),
1019 ))
1020 }
1021 }
1022 _ => {
1023 if !has_variable_before {
1024 Err(Error::new(
1025 parser.input.cur_span(),
1026 ErrorKind::Expected(
1027 "number, functions (math functions) or ident \
1028 (with 'none' value) token",
1029 ),
1030 ))
1031 } else {
1032 Ok(None)
1033 }
1034 }
1035 },
1036 &mut has_variable,
1037 )?;
1038
1039 if let Some(number_or_none) = number_or_none {
1040 values.push(number_or_none);
1041 }
1042
1043 self.input.skip_ws();
1044 }
1045 "device-cmyk" => {
1046 let cmyk_component = self.try_parse_variable_function(
1047 |parser, _| {
1048 Ok(Some(ComponentValue::CmykComponent(parser.parse()?)))
1049 },
1050 &mut has_variable,
1051 )?;
1052
1053 if let Some(cmyk_component) = cmyk_component {
1054 values.push(cmyk_component);
1055 }
1056
1057 self.input.skip_ws();
1058 }
1059 _ => {
1060 unreachable!();
1061 }
1062 }
1063 }
1064
1065 if !is_one_of!(self, EOF, "/") {
1066 match &*lower_fname {
1067 "hwb" => {
1068 let percentage_or_none = self.try_parse_variable_function(
1069 |parser, has_variable_before| match cur!(parser) {
1070 tok!("percentage") => {
1071 Ok(Some(ComponentValue::Percentage(parser.parse()?)))
1072 }
1073 Token::Function { value, .. } if is_math_function(value) => {
1074 Ok(Some(ComponentValue::Function(parser.parse()?)))
1075 }
1076 tok!("ident") => {
1077 let ident: Box<Ident> = parser.parse()?;
1078
1079 if ident.value.eq_ignore_ascii_case("none") {
1080 Ok(Some(ComponentValue::Ident(ident)))
1081 } else {
1082 Err(Error::new(
1083 ident.span,
1084 ErrorKind::Expected(
1085 "'none' value of an ident token",
1086 ),
1087 ))
1088 }
1089 }
1090 _ => {
1091 if !has_variable_before {
1092 Err(Error::new(
1093 parser.input.cur_span(),
1094 ErrorKind::Expected(
1095 "percentage, functions (math functions) or \
1096 ident (with 'none' value) token",
1097 ),
1098 ))
1099 } else {
1100 Ok(None)
1101 }
1102 }
1103 },
1104 &mut has_variable,
1105 )?;
1106
1107 if let Some(percentage_or_none) = percentage_or_none {
1108 values.push(percentage_or_none);
1109 }
1110
1111 self.input.skip_ws();
1112 }
1113 "lab" | "oklab" => {
1114 let number_or_none = self.try_parse_variable_function(
1115 |parser, has_variable_before| match cur!(parser) {
1116 tok!("percentage") => {
1117 Ok(Some(ComponentValue::Percentage(parser.parse()?)))
1118 }
1119 tok!("number") => {
1120 Ok(Some(ComponentValue::Number(parser.parse()?)))
1121 }
1122 Token::Function { value, .. } if is_math_function(value) => {
1123 Ok(Some(ComponentValue::Function(parser.parse()?)))
1124 }
1125 tok!("ident") => {
1126 let ident: Box<Ident> = parser.parse()?;
1127
1128 if ident.value.eq_ignore_ascii_case("none") {
1129 Ok(Some(ComponentValue::Ident(ident)))
1130 } else {
1131 Err(Error::new(
1132 ident.span,
1133 ErrorKind::Expected(
1134 "'none' value of an ident token",
1135 ),
1136 ))
1137 }
1138 }
1139 _ => {
1140 if !has_variable_before {
1141 Err(Error::new(
1142 parser.input.cur_span(),
1143 ErrorKind::Expected(
1144 "number, function (math functions) or ident \
1145 (with 'none' value) token",
1146 ),
1147 ))
1148 } else {
1149 Ok(None)
1150 }
1151 }
1152 },
1153 &mut has_variable,
1154 )?;
1155
1156 if let Some(number_or_none) = number_or_none {
1157 values.push(number_or_none);
1158 }
1159
1160 self.input.skip_ws();
1161 }
1162 "lch" | "oklch" => {
1163 let hue_or_none = self.try_parse_variable_function(
1164 |parser, has_variable_before| match cur!(parser) {
1165 tok!("number") | tok!("dimension") => {
1166 Ok(Some(ComponentValue::Hue(parser.parse()?)))
1167 }
1168 Token::Function { value, .. } if is_math_function(value) => {
1169 Ok(Some(ComponentValue::Function(parser.parse()?)))
1170 }
1171 tok!("ident") => {
1172 let ident: Box<Ident> = parser.parse()?;
1173
1174 if ident.value.eq_ignore_ascii_case("none") {
1175 Ok(Some(ComponentValue::Ident(ident)))
1176 } else {
1177 Err(Error::new(
1178 ident.span,
1179 ErrorKind::Expected(
1180 "'none' value of an ident token",
1181 ),
1182 ))
1183 }
1184 }
1185 _ => {
1186 if !has_variable_before {
1187 return Err(Error::new(
1188 parser.input.cur_span(),
1189 ErrorKind::Expected(
1190 "number, dimension, functions (math \
1191 functions) or ident (with 'none' value) token",
1192 ),
1193 ));
1194 } else {
1195 Ok(None)
1196 }
1197 }
1198 },
1199 &mut has_variable,
1200 )?;
1201
1202 if let Some(hue_or_none) = hue_or_none {
1203 values.push(hue_or_none);
1204 }
1205
1206 self.input.skip_ws();
1207 }
1208 "device-cmyk" => {
1209 let cmyk_component = self.try_parse_variable_function(
1210 |parser, _| {
1211 Ok(Some(ComponentValue::CmykComponent(parser.parse()?)))
1212 },
1213 &mut has_variable,
1214 )?;
1215
1216 if let Some(cmyk_component) = cmyk_component {
1217 values.push(cmyk_component);
1218 }
1219
1220 self.input.skip_ws();
1221 }
1222 _ => {
1223 unreachable!();
1224 }
1225 }
1226 }
1227
1228 if !is_one_of!(self, EOF, "/") && lower_fname == "device-cmyk" {
1229 let cmyk_component = self.try_parse_variable_function(
1230 |parser, _| Ok(Some(ComponentValue::CmykComponent(parser.parse()?))),
1231 &mut has_variable,
1232 )?;
1233
1234 if let Some(cmyk_component) = cmyk_component {
1235 values.push(cmyk_component);
1236 }
1237
1238 self.input.skip_ws();
1239 }
1240
1241 if is!(self, "/") {
1242 values.push(ComponentValue::Delimiter(self.parse()?));
1243
1244 self.input.skip_ws();
1245
1246 let alpha_value = self.try_parse_variable_function(
1247 |parser, has_variable_before| match cur!(parser) {
1248 tok!("number") | tok!("percentage") => {
1249 Ok(Some(ComponentValue::AlphaValue(parser.parse()?)))
1250 }
1251 Token::Function { value, .. } if is_math_function(value) => {
1252 Ok(Some(ComponentValue::Function(parser.parse()?)))
1253 }
1254 tok!("ident") if !matches!(&*lower_fname, "device-cmyk") => {
1255 let ident: Box<Ident> = parser.parse()?;
1256
1257 if ident.value.eq_ignore_ascii_case("none") {
1258 Ok(Some(ComponentValue::Ident(ident)))
1259 } else {
1260 Err(Error::new(
1261 ident.span,
1262 ErrorKind::Expected("'none' value of an ident token"),
1263 ))
1264 }
1265 }
1266 _ => {
1267 if !has_variable_before {
1268 Err(Error::new(
1269 parser.input.cur_span(),
1270 ErrorKind::Expected(
1271 "percentage, number, functions (math functions) or \
1272 ident (with 'none' value) token",
1273 ),
1274 ))
1275 } else {
1276 Ok(None)
1277 }
1278 }
1279 },
1280 &mut has_variable,
1281 )?;
1282
1283 if let Some(alpha_value) = alpha_value {
1284 values.push(alpha_value);
1285 }
1286
1287 self.input.skip_ws();
1288 }
1289 }
1290 "color" => {
1291 self.input.skip_ws();
1292
1293 let mut has_variable = false;
1294
1295 match cur!(self) {
1296 Token::Ident { value, .. } if matches_eq_ignore_ascii_case!(value, "from") => {
1297 values.push(ComponentValue::Ident(self.parse()?));
1298
1299 self.input.skip_ws();
1300
1301 let color = self.try_parse_variable_function(
1302 |parser, _| Ok(Some(ComponentValue::Color(parser.parse()?))),
1303 &mut has_variable,
1304 )?;
1305
1306 if let Some(color) = color {
1307 values.push(color);
1308 }
1309
1310 self.input.skip_ws();
1311 }
1312 _ => {}
1313 }
1314
1315 let mut is_custom_params = false;
1316 let mut is_xyz = false;
1317
1318 let ident = self.try_parse_variable_function(
1319 |parser, _| match cur!(parser) {
1320 Token::Ident { value, .. } => {
1321 if value.starts_with("--") && value.len() > 2 {
1322 is_custom_params = true;
1323
1324 Ok(Some(ComponentValue::DashedIdent(parser.parse()?)))
1325 } else {
1326 if matches_eq_ignore_ascii_case!(value, "xyz", "xyz-d50", "xyz-d65")
1327 {
1328 is_xyz = true
1329 } else {
1330 }
1336
1337 Ok(Some(ComponentValue::Ident(parser.parse()?)))
1338 }
1339 }
1340 _ => Err(Error::new(
1341 parser.input.cur_span(),
1342 ErrorKind::Expected("ident token"),
1343 )),
1344 },
1345 &mut has_variable,
1346 )?;
1347
1348 if let Some(ident) = ident {
1349 values.push(ident);
1350 }
1351
1352 self.input.skip_ws();
1353
1354 let number_or_percentage_or_none = self.try_parse_variable_function(
1355 |parser, has_variable_before| match cur!(parser) {
1356 tok!("number") => Ok(Some(ComponentValue::Number(parser.parse()?))),
1357 tok!("percentage") if !is_xyz => {
1358 Ok(Some(ComponentValue::Percentage(parser.parse()?)))
1359 }
1360 Token::Function { value, .. } if is_math_function(value) => {
1361 Ok(Some(ComponentValue::Function(parser.parse()?)))
1362 }
1363 tok!("ident") => {
1364 let ident: Box<Ident> = parser.parse()?;
1365
1366 if ident.value.eq_ignore_ascii_case("none") {
1367 Ok(Some(ComponentValue::Ident(ident)))
1368 } else {
1369 Err(Error::new(
1370 ident.span,
1371 ErrorKind::Expected("'none' value of an ident token"),
1372 ))
1373 }
1374 }
1375 _ => {
1376 if !has_variable_before {
1377 Err(Error::new(
1378 parser.input.cur_span(),
1379 ErrorKind::Expected(
1380 "percentage, function (math functions) or ident (with \
1381 'none' value) token",
1382 ),
1383 ))
1384 } else {
1385 Ok(None)
1386 }
1387 }
1388 },
1389 &mut has_variable,
1390 )?;
1391
1392 if let Some(number_or_percentage_or_none) = number_or_percentage_or_none {
1393 values.push(number_or_percentage_or_none);
1394 }
1395
1396 self.input.skip_ws();
1397
1398 if is_custom_params {
1399 loop {
1400 if is!(self, EOF) {
1401 break;
1402 }
1403
1404 let number_or_percentage_or_none = match cur!(self) {
1405 tok!("number") => ComponentValue::Number(self.parse()?),
1406 tok!("percentage") if !is_xyz => {
1407 ComponentValue::Percentage(self.parse()?)
1408 }
1409 Token::Function { value, .. }
1410 if is_math_function(value)
1411 || matches_eq_ignore_ascii_case!(
1412 value, "var", "env", "constant"
1413 ) =>
1414 {
1415 ComponentValue::Function(self.parse()?)
1416 }
1417 tok!("ident") => {
1418 let ident: Box<Ident> = self.parse()?;
1419
1420 if ident.value.eq_ignore_ascii_case("none") {
1421 ComponentValue::Ident(ident)
1422 } else {
1423 return Err(Error::new(
1424 ident.span,
1425 ErrorKind::Expected("'none' value of an ident token"),
1426 ));
1427 }
1428 }
1429 _ => {
1430 break;
1431 }
1432 };
1433
1434 values.push(number_or_percentage_or_none);
1435
1436 self.input.skip_ws();
1437 }
1438 } else {
1439 let number_or_percentage_or_none = self.try_parse_variable_function(
1440 |parser, has_variable_before| match cur!(parser) {
1441 tok!("number") => Ok(Some(ComponentValue::Number(parser.parse()?))),
1442 tok!("percentage") if !is_xyz => {
1443 Ok(Some(ComponentValue::Percentage(parser.parse()?)))
1444 }
1445 Token::Function { value, .. } if is_math_function(value) => {
1446 Ok(Some(ComponentValue::Function(parser.parse()?)))
1447 }
1448 tok!("ident") => {
1449 let ident: Box<Ident> = parser.parse()?;
1450
1451 if ident.value.eq_ignore_ascii_case("none") {
1452 Ok(Some(ComponentValue::Ident(ident)))
1453 } else {
1454 Err(Error::new(
1455 ident.span,
1456 ErrorKind::Expected("'none' value of an ident token"),
1457 ))
1458 }
1459 }
1460 _ => {
1461 if !has_variable_before {
1462 Err(Error::new(
1463 parser.input.cur_span(),
1464 ErrorKind::Expected(
1465 "percentage, function (math functions) or ident (with \
1466 'none' value) token",
1467 ),
1468 ))
1469 } else {
1470 Ok(None)
1471 }
1472 }
1473 },
1474 &mut has_variable,
1475 )?;
1476
1477 if let Some(number_or_percentage_or_none) = number_or_percentage_or_none {
1478 values.push(number_or_percentage_or_none);
1479 }
1480
1481 self.input.skip_ws();
1482
1483 let number_or_percentage_or_none = self.try_parse_variable_function(
1484 |parser, has_variable_before| match cur!(parser) {
1485 tok!("number") => Ok(Some(ComponentValue::Number(parser.parse()?))),
1486 tok!("percentage") if !is_xyz => {
1487 Ok(Some(ComponentValue::Percentage(parser.parse()?)))
1488 }
1489 tok!("ident") => {
1490 let ident: Box<Ident> = parser.parse()?;
1491
1492 if ident.value.eq_ignore_ascii_case("none") {
1493 Ok(Some(ComponentValue::Ident(ident)))
1494 } else {
1495 Err(Error::new(
1496 ident.span,
1497 ErrorKind::Expected("'none' value of an ident token"),
1498 ))
1499 }
1500 }
1501 Token::Function { value, .. } if is_math_function(value) => {
1502 Ok(Some(ComponentValue::Function(parser.parse()?)))
1503 }
1504 _ => {
1505 if !has_variable_before {
1506 Err(Error::new(
1507 parser.input.cur_span(),
1508 ErrorKind::Expected(
1509 "percentage, function (math functions) or ident (with \
1510 'none' value) token",
1511 ),
1512 ))
1513 } else {
1514 Ok(None)
1515 }
1516 }
1517 },
1518 &mut has_variable,
1519 )?;
1520
1521 if let Some(number_or_percentage_or_none) = number_or_percentage_or_none {
1522 values.push(number_or_percentage_or_none);
1523 }
1524
1525 self.input.skip_ws();
1526 }
1527
1528 if is!(self, "/") {
1529 values.push(ComponentValue::Delimiter(self.parse()?));
1530
1531 self.input.skip_ws();
1532
1533 let alpha_value = self.try_parse_variable_function(
1534 |parser, has_variable_before| match cur!(parser) {
1535 tok!("number") | tok!("percentage") => {
1536 Ok(Some(ComponentValue::AlphaValue(parser.parse()?)))
1537 }
1538 Token::Function { value, .. } if is_math_function(value) => {
1539 Ok(Some(ComponentValue::Function(parser.parse()?)))
1540 }
1541 tok!("ident") if !matches!(&*lower_fname, "device-cmyk") => {
1542 let ident: Box<Ident> = parser.parse()?;
1543
1544 if ident.value.eq_ignore_ascii_case("none") {
1545 Ok(Some(ComponentValue::Ident(ident)))
1546 } else {
1547 Err(Error::new(
1548 ident.span,
1549 ErrorKind::Expected("'none' value of an ident token"),
1550 ))
1551 }
1552 }
1553 _ => {
1554 if !has_variable_before {
1555 Err(Error::new(
1556 parser.input.cur_span(),
1557 ErrorKind::Expected(
1558 "percentage, number, function (math functions) or \
1559 ident (with 'none' value) token",
1560 ),
1561 ))
1562 } else {
1563 Ok(None)
1564 }
1565 }
1566 },
1567 &mut has_variable,
1568 )?;
1569
1570 if let Some(alpha_value) = alpha_value {
1571 values.push(alpha_value);
1572 }
1573
1574 self.input.skip_ws();
1575 }
1576
1577 self.input.skip_ws();
1578 }
1579 "element" | "-moz-element" => {
1580 self.input.skip_ws();
1581
1582 let id_selector = self.try_parse_variable_function(
1583 |parser, _| Ok(Some(ComponentValue::IdSelector(parser.parse()?))),
1584 &mut false,
1585 )?;
1586
1587 if let Some(id_selector) = id_selector {
1588 values.push(id_selector);
1589
1590 self.input.skip_ws();
1591 }
1592 }
1593 "selector" if self.ctx.in_supports_at_rule => {
1594 self.input.skip_ws();
1595
1596 let selector = ComponentValue::ComplexSelector(self.parse()?);
1597
1598 values.push(selector);
1599
1600 self.input.skip_ws();
1601 }
1602 "layer" if self.ctx.in_import_at_rule => {
1603 self.input.skip_ws();
1604
1605 if is!(self, EOF) {
1606 let span = self.input.cur_span();
1607
1608 return Err(Error::new(
1609 span,
1610 ErrorKind::Expected(
1611 "layer function inside @import expected to have exactly one ident \
1612 argument",
1613 ),
1614 ));
1615 }
1616
1617 let layer_name = self.parse_as::<Box<LayerName>>()?;
1618
1619 values.push(ComponentValue::LayerName(layer_name));
1620
1621 self.input.skip_ws();
1622
1623 if !is!(self, EOF) {
1624 let span = self.input.cur_span();
1625
1626 return Err(Error::new(
1627 span,
1628 ErrorKind::Expected(
1629 "layer function inside @import expected to have exactly one ident \
1630 argument",
1631 ),
1632 ));
1633 }
1634 }
1635 "supports" if self.ctx.in_import_at_rule => {
1636 self.input.skip_ws();
1637
1638 if !is!(self, EOF) {
1639 let state = self.input.state();
1640
1641 match self.parse() {
1642 Ok(declaration) => {
1643 values.push(ComponentValue::Declaration(declaration));
1644
1645 self.input.skip_ws();
1646 }
1647 Err(_) => {
1648 self.input.reset(&state);
1649
1650 let supports_conditions = self.parse()?;
1651
1652 values.push(ComponentValue::SupportsCondition(supports_conditions));
1653 }
1654 }
1655 }
1656 }
1657 _ => loop {
1658 self.input.skip_ws();
1659
1660 if is!(self, EOF) {
1661 break;
1662 }
1663
1664 let value = match self.try_parse(|p| p.parse_generic_value()) {
1665 Some(v) => v,
1666 None => {
1667 if is_one_of!(self, ";", ":") || (self.config.legacy_ie && is!(self, "=")) {
1668 let tok = self.input.bump().unwrap();
1669
1670 ComponentValue::PreservedToken(Box::new(tok))
1671 } else {
1672 return Err(Error::new(
1673 self.input.cur_span(),
1674 ErrorKind::Expected("Declaration value"),
1675 ));
1676 }
1677 }
1678 };
1679
1680 values.push(value);
1681 },
1682 };
1683
1684 Ok(values)
1685 }
1686
1687 fn try_parse_variable_function<F>(
1688 &mut self,
1689 mut fallback: F,
1690 has_before_variable: &mut bool,
1691 ) -> PResult<Option<ComponentValue>>
1692 where
1693 F: FnMut(&mut Parser<I>, bool) -> PResult<Option<ComponentValue>>,
1694 {
1695 if is!(self, EOF) {
1696 return Ok(None);
1697 }
1698
1699 match cur!(self) {
1700 Token::Function { value, .. }
1701 if matches_eq_ignore_ascii_case!(value, "var", "env", "constant") =>
1702 {
1703 *has_before_variable = true;
1704
1705 Ok(Some(ComponentValue::Function(self.parse()?)))
1706 }
1707 _ => fallback(self, *has_before_variable),
1708 }
1709 }
1710}
1711
1712impl<I> Parse<Delimiter> for Parser<I>
1713where
1714 I: ParserInput,
1715{
1716 fn parse(&mut self) -> PResult<Delimiter> {
1717 let span = self.input.cur_span();
1718
1719 if !is_one_of!(self, ",", "/", ";") {
1720 return Err(Error::new(
1721 span,
1722 ErrorKind::Expected("',', '/' or ';' delim token"),
1723 ));
1724 }
1725
1726 match cur!(self) {
1727 tok!(",") => {
1728 bump!(self);
1729
1730 return Ok(Delimiter {
1731 span: span!(self, span.lo),
1732 value: DelimiterValue::Comma,
1733 });
1734 }
1735 tok!("/") => {
1736 bump!(self);
1737
1738 return Ok(Delimiter {
1739 span: span!(self, span.lo),
1740 value: DelimiterValue::Solidus,
1741 });
1742 }
1743 tok!(";") => {
1744 bump!(self);
1745
1746 return Ok(Delimiter {
1747 span: span!(self, span.lo),
1748 value: DelimiterValue::Semicolon,
1749 });
1750 }
1751 _ => {
1752 unreachable!();
1753 }
1754 }
1755 }
1756}
1757
1758impl<I> Parse<Integer> for Parser<I>
1759where
1760 I: ParserInput,
1761{
1762 fn parse(&mut self) -> PResult<Integer> {
1763 let span = self.input.cur_span();
1764
1765 if !is!(self, Number) {
1766 return Err(Error::new(span, ErrorKind::ExpectedNumber));
1767 }
1768
1769 let value = bump!(self);
1770
1771 match value {
1772 Token::Number {
1773 value,
1774 raw,
1775 type_flag,
1776 ..
1777 } => {
1778 if type_flag == NumberType::Number {
1779 return Err(Error::new(span, ErrorKind::Expected("integer type")));
1780 }
1781
1782 Ok(Integer {
1783 span,
1784 value: value.round() as i64,
1785 raw: Some(raw),
1786 })
1787 }
1788 _ => {
1789 unreachable!()
1790 }
1791 }
1792 }
1793}
1794
1795impl<I> Parse<Number> for Parser<I>
1796where
1797 I: ParserInput,
1798{
1799 fn parse(&mut self) -> PResult<Number> {
1800 let span = self.input.cur_span();
1801
1802 if !is!(self, Number) {
1803 return Err(Error::new(span, ErrorKind::ExpectedNumber));
1804 }
1805
1806 let value = bump!(self);
1807
1808 match value {
1809 Token::Number { value, raw, .. } => Ok(Number {
1810 span,
1811 value,
1812 raw: Some(raw),
1813 }),
1814 _ => {
1815 unreachable!()
1816 }
1817 }
1818 }
1819}
1820
1821impl<I> Parse<CustomIdent> for Parser<I>
1822where
1823 I: ParserInput,
1824{
1825 fn parse(&mut self) -> PResult<CustomIdent> {
1826 let span = self.input.cur_span();
1827
1828 if !is!(self, Ident) {
1829 return Err(Error::new(span, ErrorKind::Expected("Ident")));
1830 }
1831
1832 match bump!(self) {
1833 Token::Ident { value, raw, .. } => {
1834 if matches_eq_ignore_ascii_case!(
1835 value, "initial", "inherit", "unset", "revert", "default"
1836 ) {
1837 return Err(Error::new(span, ErrorKind::InvalidCustomIdent(value)));
1838 }
1839
1840 Ok(CustomIdent {
1841 span,
1842 value,
1843 raw: Some(raw),
1844 })
1845 }
1846 _ => {
1847 unreachable!()
1848 }
1849 }
1850 }
1851}
1852
1853impl<I> Parse<DashedIdent> for Parser<I>
1854where
1855 I: ParserInput,
1856{
1857 fn parse(&mut self) -> PResult<DashedIdent> {
1858 let span = self.input.cur_span();
1859
1860 if !is!(self, Ident) {
1861 return Err(Error::new(span, ErrorKind::Expected("Ident")));
1862 }
1863
1864 match bump!(self) {
1865 Token::Ident { value, raw, .. } => {
1866 if !value.starts_with("--") {
1867 return Err(Error::new(
1868 span,
1869 ErrorKind::Expected("'--' at the start of dashed-ident"),
1870 ));
1871 }
1872
1873 if value.len() < 3 {
1874 return Err(Error::new(
1875 span,
1876 ErrorKind::Expected("dashed-ident must be at least 3 characters"),
1877 ));
1878 }
1879
1880 Ok(DashedIdent {
1881 span,
1882 value: self.input.atom(&value[2..]),
1883 raw: Some(raw),
1884 })
1885 }
1886 _ => {
1887 unreachable!()
1888 }
1889 }
1890 }
1891}
1892
1893impl<I> Parse<CustomPropertyName> for Parser<I>
1894where
1895 I: ParserInput,
1896{
1897 fn parse(&mut self) -> PResult<CustomPropertyName> {
1898 let span = self.input.cur_span();
1899
1900 if !is!(self, Ident) {
1901 return Err(Error::new(span, ErrorKind::Expected("Ident")));
1902 }
1903
1904 match bump!(self) {
1905 Token::Ident { value, raw, .. } => {
1906 if !value.starts_with("--") {
1907 return Err(Error::new(
1908 span,
1909 ErrorKind::Expected("'--' at the start of custom property name"),
1910 ));
1911 } else if &*value == "--" {
1912 return Err(Error::new(
1913 span,
1914 ErrorKind::Expected("valid dashed, '--' is not valid custom property name"),
1915 ));
1916 }
1917
1918 Ok(CustomPropertyName {
1919 span,
1920 value,
1921 raw: Some(raw),
1922 })
1923 }
1924 _ => {
1925 unreachable!()
1926 }
1927 }
1928 }
1929}
1930
1931impl<I> Parse<Ident> for Parser<I>
1932where
1933 I: ParserInput,
1934{
1935 fn parse(&mut self) -> PResult<Ident> {
1936 let span = self.input.cur_span();
1937
1938 if !is!(self, Ident) {
1939 return Err(Error::new(span, ErrorKind::Expected("Ident")));
1940 }
1941
1942 match bump!(self) {
1943 Token::Ident { value, raw, .. } => Ok(Ident {
1944 span,
1945 value,
1946 raw: Some(raw),
1947 }),
1948 _ => {
1949 unreachable!()
1950 }
1951 }
1952 }
1953}
1954
1955impl<I> Parse<Dimension> for Parser<I>
1956where
1957 I: ParserInput,
1958{
1959 fn parse(&mut self) -> PResult<Dimension> {
1960 let span = self.input.cur_span();
1961
1962 if !is!(self, Dimension) {
1963 return Err(Error::new(span, ErrorKind::Expected("dimension token")));
1964 }
1965
1966 match cur!(self) {
1967 Token::Dimension {
1968 dimension: dimension_token,
1969 } => {
1970 match &dimension_token.unit {
1971 unit if is_length_unit(unit)
1973 || (self.ctx.in_container_at_rule && is_container_lengths_unit(unit)) =>
1974 {
1975 Ok(Dimension::Length(self.parse()?))
1976 }
1977 unit if is_angle_unit(unit) => Ok(Dimension::Angle(self.parse()?)),
1979 unit if is_time_unit(unit) => Ok(Dimension::Time(self.parse()?)),
1981 unit if is_frequency_unit(unit) => Ok(Dimension::Frequency(self.parse()?)),
1983 unit if is_resolution_unit(unit) => Ok(Dimension::Resolution(self.parse()?)),
1985 unit if is_flex_unit(unit) => Ok(Dimension::Flex(self.parse()?)),
1987 _ => Ok(Dimension::UnknownDimension(self.parse()?)),
1988 }
1989 }
1990 _ => {
1991 unreachable!()
1992 }
1993 }
1994 }
1995}
1996
1997impl<I> Parse<Length> for Parser<I>
1998where
1999 I: ParserInput,
2000{
2001 fn parse(&mut self) -> PResult<Length> {
2002 let span = self.input.cur_span();
2003
2004 if !is!(self, Dimension) {
2005 return Err(Error::new(span, ErrorKind::Expected("dimension token")));
2006 }
2007
2008 match bump!(self) {
2009 Token::Dimension {
2010 dimension: dimension_token,
2011 } => {
2012 let DimensionToken {
2013 value,
2014 unit,
2015 raw_value,
2016 raw_unit,
2017 ..
2018 } = *dimension_token;
2019
2020 let unit_len = raw_unit.len() as u32;
2023
2024 Ok(Length {
2025 span,
2026 value: Number {
2027 span: Span::new(span.lo, span.hi - BytePos(unit_len)),
2028 value,
2029 raw: Some(raw_value),
2030 },
2031 unit: Ident {
2032 span: Span::new(span.hi - BytePos(unit_len), span.hi),
2033 value: unit,
2034 raw: Some(raw_unit),
2035 },
2036 })
2037 }
2038 _ => {
2039 unreachable!()
2040 }
2041 }
2042 }
2043}
2044
2045impl<I> Parse<Angle> for Parser<I>
2046where
2047 I: ParserInput,
2048{
2049 fn parse(&mut self) -> PResult<Angle> {
2050 let span = self.input.cur_span();
2051
2052 if !is!(self, Dimension) {
2053 return Err(Error::new(span, ErrorKind::Expected("dimension token")));
2054 }
2055
2056 match bump!(self) {
2057 Token::Dimension {
2058 dimension: dimension_token,
2059 } => {
2060 let DimensionToken {
2061 value,
2062 unit,
2063 raw_value,
2064 raw_unit,
2065 ..
2066 } = *dimension_token;
2067
2068 if !is_angle_unit(&unit) {
2069 return Err(Error::new(
2070 span,
2071 ErrorKind::Expected("'deg', 'grad', 'rad' or 'turn' units"),
2072 ));
2073 }
2074
2075 let unit_len = raw_unit.len() as u32;
2076
2077 Ok(Angle {
2078 span,
2079 value: Number {
2080 span: Span::new(span.lo, span.hi - BytePos(unit_len)),
2081 value,
2082 raw: Some(raw_value),
2083 },
2084 unit: Ident {
2085 span: Span::new(span.hi - BytePos(unit_len), span.hi),
2086 value: unit,
2087 raw: Some(raw_unit),
2088 },
2089 })
2090 }
2091 _ => {
2092 unreachable!()
2093 }
2094 }
2095 }
2096}
2097
2098impl<I> Parse<Time> for Parser<I>
2099where
2100 I: ParserInput,
2101{
2102 fn parse(&mut self) -> PResult<Time> {
2103 let span = self.input.cur_span();
2104
2105 if !is!(self, Dimension) {
2106 return Err(Error::new(span, ErrorKind::Expected("dimension token")));
2107 }
2108
2109 match bump!(self) {
2110 Token::Dimension {
2111 dimension: dimension_token,
2112 } => {
2113 let DimensionToken {
2114 value,
2115 unit,
2116 raw_value,
2117 raw_unit,
2118 ..
2119 } = *dimension_token;
2120
2121 if !is_time_unit(&unit) {
2122 return Err(Error::new(span, ErrorKind::Expected("'s' or 'ms' units")));
2123 }
2124
2125 let unit_len = raw_unit.len() as u32;
2126
2127 Ok(Time {
2128 span,
2129 value: Number {
2130 span: Span::new(span.lo, span.hi - BytePos(unit_len)),
2131 value,
2132 raw: Some(raw_value),
2133 },
2134 unit: Ident {
2135 span: Span::new(span.hi - BytePos(unit_len), span.hi),
2136 value: unit,
2137 raw: Some(raw_unit),
2138 },
2139 })
2140 }
2141 _ => {
2142 unreachable!()
2143 }
2144 }
2145 }
2146}
2147
2148impl<I> Parse<Frequency> for Parser<I>
2149where
2150 I: ParserInput,
2151{
2152 fn parse(&mut self) -> PResult<Frequency> {
2153 let span = self.input.cur_span();
2154
2155 if !is!(self, Dimension) {
2156 return Err(Error::new(span, ErrorKind::Expected("dimension token")));
2157 }
2158
2159 match bump!(self) {
2160 Token::Dimension {
2161 dimension: dimension_token,
2162 } => {
2163 let DimensionToken {
2164 value,
2165 unit,
2166 raw_value,
2167 raw_unit,
2168 ..
2169 } = *dimension_token;
2170
2171 if !is_frequency_unit(&unit) {
2172 return Err(Error::new(span, ErrorKind::Expected("'Hz' or 'kHz' units")));
2173 }
2174
2175 let unit_len = raw_unit.len() as u32;
2176
2177 Ok(Frequency {
2178 span,
2179 value: Number {
2180 span: Span::new(span.lo, span.hi - BytePos(unit_len)),
2181 value,
2182 raw: Some(raw_value),
2183 },
2184 unit: Ident {
2185 span: Span::new(span.hi - BytePos(unit_len), span.hi),
2186 value: unit,
2187 raw: Some(raw_unit),
2188 },
2189 })
2190 }
2191 _ => {
2192 unreachable!()
2193 }
2194 }
2195 }
2196}
2197
2198impl<I> Parse<Resolution> for Parser<I>
2199where
2200 I: ParserInput,
2201{
2202 fn parse(&mut self) -> PResult<Resolution> {
2203 let span = self.input.cur_span();
2204
2205 if !is!(self, Dimension) {
2206 return Err(Error::new(span, ErrorKind::Expected("dimension token")));
2207 }
2208
2209 match bump!(self) {
2210 Token::Dimension {
2211 dimension: dimension_token,
2212 } => {
2213 let DimensionToken {
2214 value,
2215 unit,
2216 raw_value,
2217 raw_unit,
2218 ..
2219 } = *dimension_token;
2220
2221 if !is_resolution_unit(&unit) {
2222 return Err(Error::new(
2223 span,
2224 ErrorKind::Expected("'dpi', 'dpcm', 'dppx' or 'x' units"),
2225 ));
2226 }
2227
2228 let unit_len = raw_unit.len() as u32;
2229
2230 Ok(Resolution {
2231 span,
2232 value: Number {
2233 span: Span::new(span.lo, span.hi - BytePos(unit_len)),
2234 value,
2235 raw: Some(raw_value),
2236 },
2237 unit: Ident {
2238 span: Span::new(span.hi - BytePos(unit_len), span.hi),
2239 value: unit,
2240 raw: Some(raw_unit),
2241 },
2242 })
2243 }
2244 _ => {
2245 unreachable!()
2246 }
2247 }
2248 }
2249}
2250
2251impl<I> Parse<Flex> for Parser<I>
2252where
2253 I: ParserInput,
2254{
2255 fn parse(&mut self) -> PResult<Flex> {
2256 let span = self.input.cur_span();
2257
2258 if !is!(self, Dimension) {
2259 return Err(Error::new(span, ErrorKind::Expected("dimension token")));
2260 }
2261
2262 match bump!(self) {
2263 Token::Dimension {
2264 dimension: dimension_token,
2265 } => {
2266 let DimensionToken {
2267 value,
2268 unit,
2269 raw_value,
2270 raw_unit,
2271 ..
2272 } = *dimension_token;
2273
2274 if !is_flex_unit(&unit) {
2275 return Err(Error::new(span, ErrorKind::Expected("'fr' unit")));
2276 }
2277
2278 let unit_len = raw_unit.len() as u32;
2279
2280 Ok(Flex {
2281 span,
2282 value: Number {
2283 span: Span::new(span.lo, span.hi - BytePos(unit_len)),
2284 value,
2285 raw: Some(raw_value),
2286 },
2287 unit: Ident {
2288 span: Span::new(span.hi - BytePos(unit_len), span.hi),
2289 value: unit,
2290 raw: Some(raw_unit),
2291 },
2292 })
2293 }
2294 _ => {
2295 unreachable!()
2296 }
2297 }
2298 }
2299}
2300
2301impl<I> Parse<UnknownDimension> for Parser<I>
2302where
2303 I: ParserInput,
2304{
2305 fn parse(&mut self) -> PResult<UnknownDimension> {
2306 let span = self.input.cur_span();
2307
2308 if !is!(self, Dimension) {
2309 return Err(Error::new(span, ErrorKind::Expected("dimension token")));
2310 }
2311
2312 match bump!(self) {
2313 Token::Dimension {
2314 dimension: dimension_token,
2315 } => {
2316 let DimensionToken {
2317 value,
2318 unit,
2319 raw_value,
2320 raw_unit,
2321 ..
2322 } = *dimension_token;
2323
2324 let unit_len = raw_unit.len() as u32;
2325
2326 Ok(UnknownDimension {
2327 span,
2328 value: Number {
2329 span: Span::new(span.lo, span.hi - BytePos(unit_len)),
2330 value,
2331 raw: Some(raw_value),
2332 },
2333 unit: Ident {
2334 span: Span::new(span.hi - BytePos(unit_len), span.hi),
2335 value: self.input.atom(unit.to_lowercase()),
2336 raw: Some(raw_unit),
2337 },
2338 })
2339 }
2340 _ => {
2341 unreachable!()
2342 }
2343 }
2344 }
2345}
2346
2347impl<I> Parse<Color> for Parser<I>
2348where
2349 I: ParserInput,
2350{
2351 fn parse(&mut self) -> PResult<Color> {
2352 let span = self.input.cur_span();
2353
2354 match cur!(self) {
2355 Token::Ident { value, .. }
2357 if value.as_ref().eq_ignore_ascii_case("currentcolor")
2358 || is_system_color(value) =>
2359 {
2360 Ok(Color::CurrentColorOrSystemColor(self.parse()?))
2361 }
2362 Token::Function { value, .. } if value.as_ref().eq_ignore_ascii_case("device-cmyk") => {
2364 Ok(Color::Function(self.parse()?))
2365 }
2366 _ => match self.parse() {
2368 Ok(absolute_color_base) => Ok(Color::AbsoluteColorBase(absolute_color_base)),
2369 Err(_) => {
2370 return Err(Error::new(
2371 span,
2372 ErrorKind::Expected(
2373 "hash, ident (with named color, system color, 'transparent' or \
2374 'currentColor' value) or function (with color function name) token",
2375 ),
2376 ));
2377 }
2378 },
2379 }
2380 }
2381}
2382
2383impl<I> Parse<AbsoluteColorBase> for Parser<I>
2384where
2385 I: ParserInput,
2386{
2387 fn parse(&mut self) -> PResult<AbsoluteColorBase> {
2388 let span = self.input.cur_span();
2389
2390 match cur!(self) {
2391 tok!("#") => Ok(AbsoluteColorBase::HexColor(self.parse()?)),
2392 Token::Ident { value, .. } => {
2393 if !(is_named_color(value) || value.as_ref().eq_ignore_ascii_case("transparent")) {
2394 let span = self.input.cur_span();
2395
2396 return Err(Error::new(
2397 span,
2398 ErrorKind::Expected("known named color or 'transparent' keyword"),
2399 ));
2400 }
2401
2402 Ok(AbsoluteColorBase::NamedColorOrTransparent(self.parse()?))
2403 }
2404 Token::Function { value, .. } if is_absolute_color_base_function(value) => {
2405 Ok(AbsoluteColorBase::Function(self.parse()?))
2406 }
2407 _ => {
2408 return Err(Error::new(
2409 span,
2410 ErrorKind::Expected(
2411 "hash, ident (with named color or 'transparent' value) or function (with \
2412 color function name) token",
2413 ),
2414 ));
2415 }
2416 }
2417 }
2418}
2419
2420impl<I> Parse<HexColor> for Parser<I>
2421where
2422 I: ParserInput,
2423{
2424 fn parse(&mut self) -> PResult<HexColor> {
2425 let span = self.input.cur_span();
2426
2427 if !is!(self, "#") {
2428 return Err(Error::new(span, ErrorKind::Expected("hash token")));
2429 }
2430
2431 match bump!(self) {
2432 Token::Hash { value, raw, .. } => {
2433 if value.chars().any(|x| !x.is_ascii_hexdigit()) {
2434 return Err(Error::new(
2435 span,
2436 ErrorKind::Unexpected("character in hex color"),
2437 ));
2438 }
2439
2440 Ok(HexColor {
2441 span,
2442 value,
2443 raw: Some(raw),
2444 })
2445 }
2446 _ => {
2447 unreachable!()
2448 }
2449 }
2450 }
2451}
2452
2453impl<I> Parse<AlphaValue> for Parser<I>
2454where
2455 I: ParserInput,
2456{
2457 fn parse(&mut self) -> PResult<AlphaValue> {
2458 if !is_one_of!(self, "percentage", "number") {
2459 let span = self.input.cur_span();
2460
2461 return Err(Error::new(
2462 span,
2463 ErrorKind::Expected("percentage or number token"),
2464 ));
2465 }
2466
2467 match cur!(self) {
2468 tok!("percentage") => Ok(AlphaValue::Percentage(self.parse()?)),
2469 tok!("number") => Ok(AlphaValue::Number(self.parse()?)),
2470 _ => {
2471 unreachable!()
2472 }
2473 }
2474 }
2475}
2476
2477impl<I> Parse<Hue> for Parser<I>
2478where
2479 I: ParserInput,
2480{
2481 fn parse(&mut self) -> PResult<Hue> {
2482 if !is_one_of!(self, "number", "dimension") {
2483 let span = self.input.cur_span();
2484
2485 return Err(Error::new(
2486 span,
2487 ErrorKind::Expected("number or dimension token"),
2488 ));
2489 }
2490
2491 match cur!(self) {
2492 tok!("number") => Ok(Hue::Number(self.parse()?)),
2493 tok!("dimension") => Ok(Hue::Angle(self.parse()?)),
2494 _ => {
2495 unreachable!()
2496 }
2497 }
2498 }
2499}
2500
2501impl<I> Parse<CmykComponent> for Parser<I>
2502where
2503 I: ParserInput,
2504{
2505 fn parse(&mut self) -> PResult<CmykComponent> {
2506 if !is_one_of!(self, "number", "percentage", "function") {
2507 let span = self.input.cur_span();
2508
2509 return Err(Error::new(
2510 span,
2511 ErrorKind::Expected("number, function or percentage token"),
2512 ));
2513 }
2514
2515 match cur!(self) {
2516 tok!("number") => Ok(CmykComponent::Number(self.parse()?)),
2517 tok!("percentage") => Ok(CmykComponent::Percentage(self.parse()?)),
2518 Token::Function { value, .. } => {
2519 if !is_math_function(value) {
2520 let span = self.input.cur_span();
2521
2522 return Err(Error::new(span, ErrorKind::Expected("math function token")));
2523 }
2524
2525 Ok(CmykComponent::Function(self.parse()?))
2526 }
2527 _ => {
2528 unreachable!()
2529 }
2530 }
2531 }
2532}
2533
2534impl<I> Parse<Percentage> for Parser<I>
2535where
2536 I: ParserInput,
2537{
2538 fn parse(&mut self) -> PResult<Percentage> {
2539 let span = self.input.cur_span();
2540
2541 if !is!(self, Percentage) {
2542 return Err(Error::new(span, ErrorKind::Expected("percentage token")));
2543 }
2544
2545 match bump!(self) {
2546 Token::Percentage { value, raw } => {
2547 let value = Number {
2548 span: Span::new(span.lo, span.hi - BytePos(1)),
2549 value,
2550 raw: Some(raw),
2551 };
2552
2553 Ok(Percentage { span, value })
2554 }
2555 _ => {
2556 unreachable!()
2557 }
2558 }
2559 }
2560}
2561
2562impl<I> Parse<Str> for Parser<I>
2563where
2564 I: ParserInput,
2565{
2566 fn parse(&mut self) -> PResult<Str> {
2567 let span = self.input.cur_span();
2568
2569 if !is!(self, "string") {
2570 return Err(Error::new(span, ErrorKind::Expected("string token")));
2571 }
2572
2573 match bump!(self) {
2574 Token::String { value, raw } => Ok(Str {
2575 span,
2576 value,
2577 raw: Some(raw),
2578 }),
2579 _ => {
2580 unreachable!()
2581 }
2582 }
2583 }
2584}
2585
2586impl<I> Parse<Url> for Parser<I>
2587where
2588 I: ParserInput,
2589{
2590 fn parse(&mut self) -> PResult<Url> {
2591 let span = self.input.cur_span();
2592
2593 if !is_one_of!(self, Url, Function) {
2594 return Err(Error::new(
2595 span,
2596 ErrorKind::Expected("url or function (with 'url' or 'src' name) token"),
2597 ));
2598 }
2599
2600 match bump!(self) {
2601 Token::Url { value, raw } => {
2602 let name_length = raw.0.len() as u32;
2603 let name = Ident {
2604 span: Span::new(span.lo, span.lo + BytePos(name_length)),
2605 value: self.input.atom("url"),
2606 raw: Some(raw.0),
2607 };
2608 let value = Some(Box::new(UrlValue::Raw(UrlValueRaw {
2609 span: Span::new(span.lo + BytePos(name_length + 1), span.hi - BytePos(1)),
2610 value,
2611 raw: Some(raw.1),
2612 })));
2613
2614 Ok(Url {
2615 span: span!(self, span.lo),
2616 name,
2617 value,
2618 modifiers: None,
2619 })
2620 }
2621 Token::Function {
2622 value: function_name,
2623 raw: raw_function_name,
2624 } => {
2625 if !matches_eq_ignore_ascii_case!(function_name, "url", "src") {
2626 return Err(Error::new(
2627 span,
2628 ErrorKind::Expected("'url' or 'src' name of a function token"),
2629 ));
2630 }
2631
2632 let name = Ident {
2633 span: Span::new(span.lo, span.hi - BytePos(1)),
2634 value: function_name,
2635 raw: Some(raw_function_name),
2636 };
2637
2638 self.input.skip_ws();
2639
2640 let value = match cur!(self) {
2641 tok!("string") => Some(Box::new(UrlValue::Str(self.parse()?))),
2642 _ => None,
2643 };
2644
2645 self.input.skip_ws();
2646
2647 let mut modifiers = Vec::new();
2648
2649 loop {
2650 if is!(self, ")") {
2651 break;
2652 }
2653
2654 match cur!(self) {
2655 tok!("ident") => {
2656 modifiers.push(UrlModifier::Ident(self.parse()?));
2657 }
2658 tok!("function") => {
2659 modifiers.push(UrlModifier::Function(self.parse()?));
2660 }
2661 _ => {
2662 let span = self.input.cur_span();
2663
2664 return Err(Error::new(span, ErrorKind::Expected("ident or function")));
2665 }
2666 }
2667
2668 self.input.skip_ws();
2669 }
2670
2671 expect!(self, ")");
2672
2673 Ok(Url {
2674 span: span!(self, span.lo),
2675 name,
2676 value,
2677 modifiers: Some(modifiers),
2678 })
2679 }
2680 _ => {
2681 unreachable!()
2682 }
2683 }
2684 }
2685}
2686
2687impl<I> Parse<UnicodeRange> for Parser<I>
2695where
2696 I: ParserInput,
2697{
2698 fn parse(&mut self) -> PResult<UnicodeRange> {
2699 let span = self.input.cur_span();
2700 let mut unicode_range = String::new();
2701
2702 match cur!(self) {
2704 Token::Ident { value, .. } if matches_eq_ignore_ascii_case!(value, "u") => {
2705 let u = match bump!(self) {
2706 Token::Ident { value, .. } => value,
2707 _ => {
2708 unreachable!();
2709 }
2710 };
2711
2712 unicode_range.push_str(&u);
2713 }
2714 _ => {
2715 return Err(Error::new(span, ErrorKind::Expected("'u' ident token")));
2716 }
2717 };
2718
2719 match cur!(self) {
2720 Token::Delim { value } if *value == '+' => {
2723 let plus = match bump!(self) {
2724 Token::Delim { value } => value,
2725 _ => {
2726 unreachable!();
2727 }
2728 };
2729
2730 unicode_range.push(plus);
2731
2732 if is!(self, Ident) {
2733 let ident = match bump!(self) {
2734 Token::Ident { value, .. } => value,
2735 _ => {
2736 unreachable!();
2737 }
2738 };
2739
2740 unicode_range.push_str(&ident);
2741
2742 loop {
2743 if !is!(self, "?") {
2744 break;
2745 }
2746
2747 let question = match bump!(self) {
2748 Token::Delim { value } => value,
2749 _ => {
2750 unreachable!();
2751 }
2752 };
2753
2754 unicode_range.push(question);
2755 }
2756 } else {
2757 let question = match bump!(self) {
2758 Token::Delim { value } if value == '?' => value,
2759 _ => {
2760 return Err(Error::new(span, ErrorKind::Expected("'?' delim token")));
2761 }
2762 };
2763
2764 unicode_range.push(question);
2765
2766 loop {
2767 if !is!(self, "?") {
2768 break;
2769 }
2770
2771 let question = match bump!(self) {
2772 Token::Delim { value } => value,
2773 _ => {
2774 unreachable!();
2775 }
2776 };
2777
2778 unicode_range.push(question);
2779 }
2780 }
2781 }
2782 tok!("number") => {
2786 let number = match bump!(self) {
2787 Token::Number { raw, .. } => raw,
2788 _ => {
2789 unreachable!();
2790 }
2791 };
2792
2793 unicode_range.push_str(&number);
2794
2795 if !is!(self, EOF) {
2796 match cur!(self) {
2797 tok!("?") => {
2798 let question = match bump!(self) {
2799 Token::Delim { value } => value,
2800 _ => {
2801 unreachable!();
2802 }
2803 };
2804
2805 unicode_range.push(question);
2806
2807 loop {
2808 if !is!(self, "?") {
2809 break;
2810 }
2811
2812 let question = match bump!(self) {
2813 Token::Delim { value } => value,
2814 _ => {
2815 unreachable!();
2816 }
2817 };
2818
2819 unicode_range.push(question);
2820 }
2821 }
2822 tok!("dimension") => {
2823 let raw = match bump!(self) {
2824 Token::Dimension {
2825 dimension: dimension_token,
2826 } => (dimension_token.raw_value, dimension_token.raw_unit),
2827 _ => {
2828 unreachable!();
2829 }
2830 };
2831
2832 unicode_range.push_str(&raw.0);
2833 unicode_range.push_str(&raw.1);
2834 }
2835 tok!("number") => {
2836 let number = match bump!(self) {
2837 Token::Number { raw, .. } => raw,
2838 _ => {
2839 unreachable!();
2840 }
2841 };
2842
2843 unicode_range.push_str(&number);
2844 }
2845 _ => {}
2846 }
2847 }
2848 }
2849 tok!("dimension") => {
2851 let raw = match bump!(self) {
2852 Token::Dimension {
2853 dimension: dimension_token,
2854 } => (dimension_token.raw_value, dimension_token.raw_unit),
2855 _ => {
2856 unreachable!();
2857 }
2858 };
2859
2860 unicode_range.push_str(&raw.0);
2861 unicode_range.push_str(&raw.1);
2862
2863 loop {
2864 if !is!(self, "?") {
2865 break;
2866 }
2867
2868 let question = match bump!(self) {
2869 Token::Delim { value } => value,
2870 _ => {
2871 unreachable!();
2872 }
2873 };
2874
2875 unicode_range.push(question);
2876 }
2877 }
2878 _ => {
2879 return Err(Error::new(
2880 span,
2881 ErrorKind::Expected("dimension, number or '?' delim token"),
2882 ));
2883 }
2884 }
2885
2886 let mut chars = unicode_range.chars();
2887
2888 chars.next();
2891
2892 let mut next = chars.next();
2895
2896 if next != Some('+') {
2897 return Err(Error::new(
2898 span,
2899 ErrorKind::Expected("'+' character after 'u' in unicode range"),
2900 ));
2901 } else {
2902 next = chars.next();
2903 }
2904
2905 let mut start = String::new();
2910
2911 loop {
2912 match next {
2913 Some(c) if c.is_ascii_digit() => {
2914 start.push(c);
2915
2916 next = chars.next();
2917 }
2918 Some(c @ 'A'..='F') | Some(c @ 'a'..='f') => {
2919 start.push(c);
2920
2921 next = chars.next();
2922 }
2923 _ => {
2924 break;
2925 }
2926 }
2927 }
2928
2929 let mut has_question_mark = false;
2930
2931 while let Some(c @ '?') = next {
2932 has_question_mark = true;
2933
2934 start.push(c);
2935
2936 next = chars.next();
2937 }
2938
2939 let len = start.len();
2940
2941 if len == 0 || len > 6 {
2942 return Err(Error::new(
2943 span,
2944 ErrorKind::Expected(
2945 "valid length (minimum 1 or maximum 6 hex digits) in the start of unicode \
2946 range",
2947 ),
2948 ));
2949 }
2950
2951 if has_question_mark {
2953 if next.is_some() {
2956 return Err(Error::new(
2957 span,
2958 ErrorKind::Expected("no characters after '?' in unicode range"),
2959 ));
2960 }
2961
2962 return Ok(UnicodeRange {
2973 span: span!(self, span.lo),
2974 start: self.input.atom(start),
2975 end: None,
2976 raw: Some(self.input.atom(unicode_range)),
2977 });
2978 }
2979
2980 if next.is_none() {
2986 return Ok(UnicodeRange {
2987 span: span!(self, span.lo),
2988 start: self.input.atom(start),
2989 end: None,
2990 raw: Some(self.input.atom(unicode_range)),
2991 });
2992 }
2993
2994 if next != Some('-') {
2997 return Err(Error::new(
2998 span,
2999 ErrorKind::Expected("'-' between start and end in unicode range"),
3000 ));
3001 } else {
3002 next = chars.next();
3003 }
3004
3005 let mut end = String::new();
3011
3012 loop {
3013 match next {
3014 Some(c) if c.is_ascii_digit() => {
3015 end.push(c);
3016 next = chars.next();
3017 }
3018 Some(c @ 'A'..='F') | Some(c @ 'a'..='f') => {
3019 end.push(c);
3020 next = chars.next();
3021 }
3022 _ => {
3023 break;
3024 }
3025 }
3026 }
3027
3028 let len = end.len();
3029
3030 if len == 0 || len > 6 {
3031 return Err(Error::new(
3032 span,
3033 ErrorKind::Expected(
3034 "valid length (minimum 1 or maximum 6 hex digits) in the end of unicode range",
3035 ),
3036 ));
3037 }
3038
3039 if chars.next().is_some() {
3040 return Err(Error::new(
3041 span,
3042 ErrorKind::Expected("no characters after end in unicode range"),
3043 ));
3044 }
3045
3046 return Ok(UnicodeRange {
3047 span: span!(self, span.lo),
3048 start: self.input.atom(start),
3049 end: Some(self.input.atom(end)),
3050 raw: Some(self.input.atom(unicode_range)),
3051 });
3052 }
3053}
3054
3055impl<I> Parse<CalcSum> for Parser<I>
3056where
3057 I: ParserInput,
3058{
3059 fn parse(&mut self) -> PResult<CalcSum> {
3060 let start = self.input.cur_span().lo;
3061 let mut expressions = Vec::new();
3062 let calc_product = CalcProductOrOperator::Product(self.parse()?);
3063 let mut end = match calc_product {
3064 CalcProductOrOperator::Product(ref calc_product) => calc_product.span.hi,
3065 _ => {
3066 unreachable!();
3067 }
3068 };
3069
3070 expressions.push(calc_product);
3071
3072 loop {
3073 self.input.skip_ws();
3074
3075 if is!(self, EOF) {
3076 break;
3077 }
3078
3079 match cur!(self) {
3080 tok!("+") | tok!("-") => {
3081 let operator = CalcProductOrOperator::Operator(self.parse()?);
3082
3083 expressions.push(operator);
3084
3085 self.input.skip_ws();
3086
3087 let calc_product = CalcProductOrOperator::Product(self.parse()?);
3088
3089 end = match calc_product {
3090 CalcProductOrOperator::Product(ref calc_product) => calc_product.span.hi,
3091 _ => {
3092 unreachable!();
3093 }
3094 };
3095
3096 expressions.push(calc_product);
3097 }
3098 _ => {
3099 break;
3100 }
3101 }
3102 }
3103
3104 Ok(CalcSum {
3105 span: Span::new(start, end),
3106 expressions,
3107 })
3108 }
3109}
3110
3111impl<I> Parse<CalcProduct> for Parser<I>
3112where
3113 I: ParserInput,
3114{
3115 fn parse(&mut self) -> PResult<CalcProduct> {
3116 let start = self.input.cur_span().lo;
3117 let mut expressions = Vec::new();
3118 let calc_value = CalcValueOrOperator::Value(self.parse()?);
3119 let mut end = match calc_value {
3120 CalcValueOrOperator::Value(ref calc_value) => match calc_value {
3121 CalcValue::Number(item) => item.span.hi,
3122 CalcValue::Percentage(item) => item.span.hi,
3123 CalcValue::Constant(item) => item.span.hi,
3124 CalcValue::Sum(item) => item.span.hi,
3125 CalcValue::Function(item) => item.span.hi,
3126 CalcValue::Dimension(item) => match item {
3127 Dimension::Length(item) => item.span.hi,
3128 Dimension::Angle(item) => item.span.hi,
3129 Dimension::Time(item) => item.span.hi,
3130 Dimension::Frequency(item) => item.span.hi,
3131 Dimension::Resolution(item) => item.span.hi,
3132 Dimension::Flex(item) => item.span.hi,
3133 Dimension::UnknownDimension(item) => item.span.hi,
3134 },
3135 },
3136 _ => {
3137 unreachable!();
3138 }
3139 };
3140
3141 expressions.push(calc_value);
3142
3143 loop {
3144 self.input.skip_ws();
3145
3146 if is!(self, EOF) {
3147 break;
3148 }
3149
3150 match cur!(self) {
3151 tok!("*") | tok!("/") => {
3152 let operator = CalcValueOrOperator::Operator(self.parse()?);
3153
3154 expressions.push(operator);
3155
3156 self.input.skip_ws();
3157
3158 let calc_value = CalcValueOrOperator::Value(self.parse()?);
3159
3160 end = match calc_value {
3161 CalcValueOrOperator::Value(ref calc_value) => match calc_value {
3162 CalcValue::Number(item) => item.span.hi,
3163 CalcValue::Percentage(item) => item.span.hi,
3164 CalcValue::Constant(item) => item.span.hi,
3165 CalcValue::Sum(item) => item.span.hi,
3166 CalcValue::Function(item) => item.span.hi,
3167 CalcValue::Dimension(item) => match item {
3168 Dimension::Length(item) => item.span.hi,
3169 Dimension::Angle(item) => item.span.hi,
3170 Dimension::Time(item) => item.span.hi,
3171 Dimension::Frequency(item) => item.span.hi,
3172 Dimension::Resolution(item) => item.span.hi,
3173 Dimension::Flex(item) => item.span.hi,
3174 Dimension::UnknownDimension(item) => item.span.hi,
3175 },
3176 },
3177 _ => {
3178 unreachable!();
3179 }
3180 };
3181
3182 expressions.push(calc_value);
3183 }
3184 _ => {
3185 break;
3186 }
3187 }
3188 }
3189
3190 Ok(CalcProduct {
3191 span: Span::new(start, end),
3192 expressions,
3193 })
3194 }
3195}
3196
3197impl<I> Parse<CalcOperator> for Parser<I>
3198where
3199 I: ParserInput,
3200{
3201 fn parse(&mut self) -> PResult<CalcOperator> {
3202 let span = self.input.cur_span();
3203
3204 match cur!(self) {
3205 tok!("+") => {
3206 bump!(self);
3207
3208 Ok(CalcOperator {
3209 span: span!(self, span.lo),
3210 value: CalcOperatorType::Add,
3211 })
3212 }
3213 tok!("-") => {
3214 bump!(self);
3215
3216 Ok(CalcOperator {
3217 span: span!(self, span.lo),
3218 value: CalcOperatorType::Sub,
3219 })
3220 }
3221 tok!("*") => {
3222 bump!(self);
3223
3224 Ok(CalcOperator {
3225 span: span!(self, span.lo),
3226 value: CalcOperatorType::Mul,
3227 })
3228 }
3229 tok!("/") => {
3230 bump!(self);
3231
3232 Ok(CalcOperator {
3233 span: span!(self, span.lo),
3234 value: CalcOperatorType::Div,
3235 })
3236 }
3237 _ => {
3238 let span = self.input.cur_span();
3239
3240 return Err(Error::new(
3241 span,
3242 ErrorKind::Expected("'+', '-', '*' or '/' delim tokens"),
3243 ));
3244 }
3245 }
3246 }
3247}
3248
3249impl<I> Parse<CalcValue> for Parser<I>
3250where
3251 I: ParserInput,
3252{
3253 fn parse(&mut self) -> PResult<CalcValue> {
3254 match cur!(self) {
3255 tok!("number") => Ok(CalcValue::Number(self.parse()?)),
3256 tok!("dimension") => Ok(CalcValue::Dimension(self.parse()?)),
3257 tok!("percentage") => Ok(CalcValue::Percentage(self.parse()?)),
3258 Token::Ident { value, .. } => {
3259 match &*value.to_ascii_lowercase() {
3260 "e" | "pi" | "infinity" | "-infinity" | "nan" => {}
3261 _ => {
3262 let span = self.input.cur_span();
3263
3264 return Err(Error::new(
3265 span,
3266 ErrorKind::Expected(
3267 "'e', 'pi', 'infinity', '-infinity' or 'NaN', ident tokens",
3268 ),
3269 ));
3270 }
3271 }
3272
3273 Ok(CalcValue::Constant(self.parse()?))
3274 }
3275 tok!("(") => {
3276 let span = self.input.cur_span();
3277
3278 expect!(self, "(");
3279
3280 self.input.skip_ws();
3281
3282 let mut calc_sum_in_parens: CalcSum = self.parse()?;
3283
3284 self.input.skip_ws();
3285
3286 expect!(self, ")");
3287
3288 calc_sum_in_parens.span = span!(self, span.lo);
3289
3290 Ok(CalcValue::Sum(calc_sum_in_parens))
3291 }
3292 tok!("function") => Ok(CalcValue::Function(self.parse()?)),
3293 _ => {
3294 let span = self.input.cur_span();
3295
3296 return Err(Error::new(
3297 span,
3298 ErrorKind::Expected(
3299 "'number', 'dimension', 'percentage', 'ident', '(' or 'function' tokens",
3300 ),
3301 ));
3302 }
3303 }
3304 }
3305}
3306
3307impl<I> Parse<FamilyName> for Parser<I>
3308where
3309 I: ParserInput,
3310{
3311 fn parse(&mut self) -> PResult<FamilyName> {
3312 match cur!(self) {
3313 tok!("string") => Ok(FamilyName::Str(self.parse()?)),
3314 tok!("ident") => {
3315 let span = self.input.cur_span();
3316
3317 let mut value = vec![self.parse()?];
3318
3319 loop {
3320 self.input.skip_ws();
3321
3322 if !is!(self, "ident") {
3323 break;
3324 }
3325
3326 value.push(self.parse()?);
3327 }
3328
3329 Ok(FamilyName::SequenceOfCustomIdents(SequenceOfCustomIdents {
3330 span: span!(self, span.lo),
3331 value,
3332 }))
3333 }
3334 _ => {
3335 let span = self.input.cur_span();
3336
3337 return Err(Error::new(
3338 span,
3339 ErrorKind::Expected("'string' or 'ident' tokens"),
3340 ));
3341 }
3342 }
3343 }
3344}
3345
3346pub(crate) fn is_math_function(name: &Atom) -> bool {
3347 matches_eq_ignore_ascii_case!(
3348 name,
3349 "calc",
3350 "-moz-calc",
3351 "-webkit-calc",
3352 "sin",
3353 "cos",
3354 "tan",
3355 "asin",
3356 "acos",
3357 "atan",
3358 "sqrt",
3359 "exp",
3360 "abs",
3361 "sign",
3362 "min",
3363 "max",
3364 "hypot",
3365 "clamp",
3366 "round",
3367 "mod",
3368 "rem",
3369 "atan2",
3370 "pow",
3371 "log"
3372 )
3373}
3374
3375fn is_absolute_color_base_function(name: &Atom) -> bool {
3376 matches_eq_ignore_ascii_case!(
3377 name,
3378 "rgb",
3379 "rgba",
3380 "hsl",
3381 "hsla",
3382 "hwb",
3383 "lab",
3384 "lch",
3385 "oklab",
3386 "oklch",
3387 "color",
3388 "color-mix",
3389 "color-contrast"
3390 )
3391}
3392
3393fn is_system_color(name: &Atom) -> bool {
3394 matches_eq_ignore_ascii_case!(
3395 name,
3396 "canvas",
3397 "canvastext",
3398 "linktext",
3399 "visitedtext",
3400 "activetext",
3401 "buttonface",
3402 "buttontext",
3403 "buttonborder",
3404 "field",
3405 "fieldtext",
3406 "highlight",
3407 "highlighttext",
3408 "selecteditem",
3409 "selecteditemtext",
3410 "mark",
3411 "marktext",
3412 "graytext",
3413 "activeborder",
3415 "activecaption",
3416 "appWorkspace",
3417 "background",
3418 "buttonhighlight",
3419 "buttonshadow",
3420 "captiontext",
3421 "inactiveborder",
3422 "inactivecaption",
3423 "inactivecaptiontext",
3424 "infobackground",
3425 "infotext",
3426 "menu",
3427 "menutext",
3428 "scrollbar",
3429 "threeddarkshadow",
3430 "threedface",
3431 "threedhighlight",
3432 "threedlightshadow",
3433 "threedshadow",
3434 "window",
3435 "windowframe",
3436 "windowtext",
3437 "-moz-buttondefault",
3439 "-moz-buttonhoverface",
3440 "-moz-buttonhovertext",
3441 "-moz-cellhighlight",
3442 "-moz-cellhighlighttext",
3443 "-moz-combobox",
3444 "-moz-comboboxtext",
3445 "-moz-dialog",
3446 "-moz-dialogtext",
3447 "-moz-dragtargetzone",
3448 "-moz-eventreerow",
3449 "-moz-html-cellhighlight",
3450 "-moz-html-cellhighlighttext",
3451 "-moz-mac-accentdarkestshadow",
3452 "-moz-mac-accentdarkshadow",
3453 "-moz-mac-accentface",
3454 "-moz-mac-accentlightesthighlight",
3455 "-moz-mac-accentlightshadow",
3456 "-moz-mac-accentregularhighlight",
3457 "-moz-mac-accentregularshadow",
3458 "-moz-mac-chrome-active",
3459 "-moz-mac-chrome-inactive",
3460 "-moz-mac-focusring",
3461 "-moz-mac-menuselect",
3462 "-moz-mac-menushadow",
3463 "-moz-mac-menutextselect",
3464 "-moz-menuhover",
3465 "-moz-menuhovertext",
3466 "-moz-menubartext",
3467 "-moz-menubarhovertext",
3468 "-moz-nativehyperlinktext",
3469 "-moz-oddtreerow",
3470 "-moz-win-communicationstext",
3471 "-moz-win-mediatext",
3472 "-moz-win-accentcolor",
3473 "-moz-win-accentcolortext",
3474 "-moz-activehyperlinktext",
3476 "-moz-default-background-color",
3477 "-moz-default-color",
3478 "-moz-hyperlinktext",
3479 "-moz-visitedhyperlinktext"
3480 )
3481}
3482
3483fn is_named_color(name: &Atom) -> bool {
3484 matches_eq_ignore_ascii_case!(
3485 name,
3486 "aliceblue",
3487 "antiquewhite",
3488 "aqua",
3489 "aquamarine",
3490 "azure",
3491 "beige",
3492 "bisque",
3493 "black",
3494 "blanchedalmond",
3495 "blue",
3496 "blueviolet",
3497 "brown",
3498 "burlywood",
3499 "cadetblue",
3500 "chartreuse",
3501 "chocolate",
3502 "coral",
3503 "cornflowerblue",
3504 "cornsilk",
3505 "crimson",
3506 "cyan",
3507 "darkblue",
3508 "darkcyan",
3509 "darkgoldenrod",
3510 "darkgray",
3511 "darkgreen",
3512 "darkgrey",
3513 "darkkhaki",
3514 "darkmagenta",
3515 "darkolivegreen",
3516 "darkorange",
3517 "darkorchid",
3518 "darkred",
3519 "darksalmon",
3520 "darkseagreen",
3521 "darkslateblue",
3522 "darkslategray",
3523 "darkslategrey",
3524 "darkturquoise",
3525 "darkviolet",
3526 "deeppink",
3527 "deepskyblue",
3528 "dimgray",
3529 "dimgrey",
3530 "dodgerblue",
3531 "firebrick",
3532 "floralwhite",
3533 "forestgreen",
3534 "fuchsia",
3535 "gainsboro",
3536 "ghostwhite",
3537 "gold",
3538 "goldenrod",
3539 "gray",
3540 "green",
3541 "greenyellow",
3542 "grey",
3543 "honeydew",
3544 "hotpink",
3545 "indianred",
3546 "indigo",
3547 "ivory",
3548 "khaki",
3549 "lavender",
3550 "lavenderblush",
3551 "lawngreen",
3552 "lemonchiffon",
3553 "lightblue",
3554 "lightcoral",
3555 "lightcyan",
3556 "lightgoldenrodyellow",
3557 "lightgray",
3558 "lightgreen",
3559 "lightgrey",
3560 "lightpink",
3561 "lightsalmon",
3562 "lightseagreen",
3563 "lightskyblue",
3564 "lightslategray",
3565 "lightslategrey",
3566 "lightsteelblue",
3567 "lightyellow",
3568 "lime",
3569 "limegreen",
3570 "linen",
3571 "magenta",
3572 "maroon",
3573 "mediumaquamarine",
3574 "mediumblue",
3575 "mediumorchid",
3576 "mediumpurple",
3577 "mediumseagreen",
3578 "mediumslateblue",
3579 "mediumspringgreen",
3580 "mediumturquoise",
3581 "mediumvioletred",
3582 "midnightblue",
3583 "mintcream",
3584 "mistyrose",
3585 "moccasin",
3586 "navajowhite",
3587 "navy",
3588 "oldlace",
3589 "olive",
3590 "olivedrab",
3591 "orange",
3592 "orangered",
3593 "orchid",
3594 "palegoldenrod",
3595 "palegreen",
3596 "paleturquoise",
3597 "palevioletred",
3598 "papayawhip",
3599 "peachpuff",
3600 "peru",
3601 "pink",
3602 "plum",
3603 "powderblue",
3604 "purple",
3605 "rebeccapurple",
3606 "red",
3607 "rosybrown",
3608 "royalblue",
3609 "saddlebrown",
3610 "salmon",
3611 "sandybrown",
3612 "seagreen",
3613 "seashell",
3614 "sienna",
3615 "silver",
3616 "skyblue",
3617 "slateblue",
3618 "slategray",
3619 "slategrey",
3620 "snow",
3621 "springgreen",
3622 "steelblue",
3623 "tan",
3624 "teal",
3625 "thistle",
3626 "tomato",
3627 "turquoise",
3628 "violet",
3629 "wheat",
3630 "white",
3631 "whitesmoke",
3632 "yellow",
3633 "yellowgreen"
3634 )
3635}
3636
3637fn is_length_unit(unit: &Atom) -> bool {
3638 matches_eq_ignore_ascii_case!(
3639 unit, "em", "rem", "ex", "rex", "cap", "rcap", "ch", "rch", "ic", "ric", "lh", "rlh",
3640 "vw", "svw", "lvw", "dvw", "vh", "svh", "lvh", "dvh", "vi", "svi", "lvi", "dvi", "vb",
3642 "svb", "lvb", "dvb", "vmin", "svmin", "lvmin", "dvmin", "vmax", "svmax", "lvmax", "dvmax",
3643 "cm", "mm", "q", "in", "pc", "pt", "px", "mozmm"
3645 )
3646}
3647
3648fn is_container_lengths_unit(unit: &Atom) -> bool {
3649 matches_eq_ignore_ascii_case!(unit, "cqw", "cqh", "cqi", "cqb", "cqmin", "cqmax")
3650}
3651
3652fn is_angle_unit(unit: &Atom) -> bool {
3653 matches_eq_ignore_ascii_case!(unit, "deg", "grad", "rad", "turn")
3654}
3655
3656fn is_time_unit(unit: &Atom) -> bool {
3657 matches_eq_ignore_ascii_case!(unit, "s", "ms")
3658}
3659
3660fn is_frequency_unit(unit: &Atom) -> bool {
3661 matches_eq_ignore_ascii_case!(unit, "hz", "khz")
3662}
3663
3664fn is_resolution_unit(unit: &Atom) -> bool {
3665 matches_eq_ignore_ascii_case!(unit, "dpi", "dpcm", "dppx", "x")
3666}
3667
3668fn is_flex_unit(unit: &Atom) -> bool {
3669 matches_eq_ignore_ascii_case!(unit, "fr")
3670}