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(dimension_token) => {
1968 match &dimension_token.unit {
1969 unit if is_length_unit(unit)
1971 || (self.ctx.in_container_at_rule && is_container_lengths_unit(unit)) =>
1972 {
1973 Ok(Dimension::Length(self.parse()?))
1974 }
1975 unit if is_angle_unit(unit) => Ok(Dimension::Angle(self.parse()?)),
1977 unit if is_time_unit(unit) => Ok(Dimension::Time(self.parse()?)),
1979 unit if is_frequency_unit(unit) => Ok(Dimension::Frequency(self.parse()?)),
1981 unit if is_resolution_unit(unit) => Ok(Dimension::Resolution(self.parse()?)),
1983 unit if is_flex_unit(unit) => Ok(Dimension::Flex(self.parse()?)),
1985 _ => Ok(Dimension::UnknownDimension(self.parse()?)),
1986 }
1987 }
1988 _ => {
1989 unreachable!()
1990 }
1991 }
1992 }
1993}
1994
1995impl<I> Parse<Length> for Parser<I>
1996where
1997 I: ParserInput,
1998{
1999 fn parse(&mut self) -> PResult<Length> {
2000 let span = self.input.cur_span();
2001
2002 if !is!(self, Dimension) {
2003 return Err(Error::new(span, ErrorKind::Expected("dimension token")));
2004 }
2005
2006 match bump!(self) {
2007 Token::Dimension(dimension_token) => {
2008 let DimensionToken {
2009 value,
2010 unit,
2011 raw_value,
2012 raw_unit,
2013 ..
2014 } = *dimension_token;
2015
2016 let unit_len = raw_unit.len() as u32;
2019
2020 Ok(Length {
2021 span,
2022 value: Number {
2023 span: Span::new(span.lo, span.hi - BytePos(unit_len)),
2024 value,
2025 raw: Some(raw_value),
2026 },
2027 unit: Ident {
2028 span: Span::new(span.hi - BytePos(unit_len), span.hi),
2029 value: unit,
2030 raw: Some(raw_unit),
2031 },
2032 })
2033 }
2034 _ => {
2035 unreachable!()
2036 }
2037 }
2038 }
2039}
2040
2041impl<I> Parse<Angle> for Parser<I>
2042where
2043 I: ParserInput,
2044{
2045 fn parse(&mut self) -> PResult<Angle> {
2046 let span = self.input.cur_span();
2047
2048 if !is!(self, Dimension) {
2049 return Err(Error::new(span, ErrorKind::Expected("dimension token")));
2050 }
2051
2052 match bump!(self) {
2053 Token::Dimension(dimension_token) => {
2054 let DimensionToken {
2055 value,
2056 unit,
2057 raw_value,
2058 raw_unit,
2059 ..
2060 } = *dimension_token;
2061
2062 if !is_angle_unit(&unit) {
2063 return Err(Error::new(
2064 span,
2065 ErrorKind::Expected("'deg', 'grad', 'rad' or 'turn' units"),
2066 ));
2067 }
2068
2069 let unit_len = raw_unit.len() as u32;
2070
2071 Ok(Angle {
2072 span,
2073 value: Number {
2074 span: Span::new(span.lo, span.hi - BytePos(unit_len)),
2075 value,
2076 raw: Some(raw_value),
2077 },
2078 unit: Ident {
2079 span: Span::new(span.hi - BytePos(unit_len), span.hi),
2080 value: unit,
2081 raw: Some(raw_unit),
2082 },
2083 })
2084 }
2085 _ => {
2086 unreachable!()
2087 }
2088 }
2089 }
2090}
2091
2092impl<I> Parse<Time> for Parser<I>
2093where
2094 I: ParserInput,
2095{
2096 fn parse(&mut self) -> PResult<Time> {
2097 let span = self.input.cur_span();
2098
2099 if !is!(self, Dimension) {
2100 return Err(Error::new(span, ErrorKind::Expected("dimension token")));
2101 }
2102
2103 match bump!(self) {
2104 Token::Dimension(dimension_token) => {
2105 let DimensionToken {
2106 value,
2107 unit,
2108 raw_value,
2109 raw_unit,
2110 ..
2111 } = *dimension_token;
2112
2113 if !is_time_unit(&unit) {
2114 return Err(Error::new(span, ErrorKind::Expected("'s' or 'ms' units")));
2115 }
2116
2117 let unit_len = raw_unit.len() as u32;
2118
2119 Ok(Time {
2120 span,
2121 value: Number {
2122 span: Span::new(span.lo, span.hi - BytePos(unit_len)),
2123 value,
2124 raw: Some(raw_value),
2125 },
2126 unit: Ident {
2127 span: Span::new(span.hi - BytePos(unit_len), span.hi),
2128 value: unit,
2129 raw: Some(raw_unit),
2130 },
2131 })
2132 }
2133 _ => {
2134 unreachable!()
2135 }
2136 }
2137 }
2138}
2139
2140impl<I> Parse<Frequency> for Parser<I>
2141where
2142 I: ParserInput,
2143{
2144 fn parse(&mut self) -> PResult<Frequency> {
2145 let span = self.input.cur_span();
2146
2147 if !is!(self, Dimension) {
2148 return Err(Error::new(span, ErrorKind::Expected("dimension token")));
2149 }
2150
2151 match bump!(self) {
2152 Token::Dimension(dimension_token) => {
2153 let DimensionToken {
2154 value,
2155 unit,
2156 raw_value,
2157 raw_unit,
2158 ..
2159 } = *dimension_token;
2160
2161 if !is_frequency_unit(&unit) {
2162 return Err(Error::new(span, ErrorKind::Expected("'Hz' or 'kHz' units")));
2163 }
2164
2165 let unit_len = raw_unit.len() as u32;
2166
2167 Ok(Frequency {
2168 span,
2169 value: Number {
2170 span: Span::new(span.lo, span.hi - BytePos(unit_len)),
2171 value,
2172 raw: Some(raw_value),
2173 },
2174 unit: Ident {
2175 span: Span::new(span.hi - BytePos(unit_len), span.hi),
2176 value: unit,
2177 raw: Some(raw_unit),
2178 },
2179 })
2180 }
2181 _ => {
2182 unreachable!()
2183 }
2184 }
2185 }
2186}
2187
2188impl<I> Parse<Resolution> for Parser<I>
2189where
2190 I: ParserInput,
2191{
2192 fn parse(&mut self) -> PResult<Resolution> {
2193 let span = self.input.cur_span();
2194
2195 if !is!(self, Dimension) {
2196 return Err(Error::new(span, ErrorKind::Expected("dimension token")));
2197 }
2198
2199 match bump!(self) {
2200 Token::Dimension(dimension_token) => {
2201 let DimensionToken {
2202 value,
2203 unit,
2204 raw_value,
2205 raw_unit,
2206 ..
2207 } = *dimension_token;
2208
2209 if !is_resolution_unit(&unit) {
2210 return Err(Error::new(
2211 span,
2212 ErrorKind::Expected("'dpi', 'dpcm', 'dppx' or 'x' units"),
2213 ));
2214 }
2215
2216 let unit_len = raw_unit.len() as u32;
2217
2218 Ok(Resolution {
2219 span,
2220 value: Number {
2221 span: Span::new(span.lo, span.hi - BytePos(unit_len)),
2222 value,
2223 raw: Some(raw_value),
2224 },
2225 unit: Ident {
2226 span: Span::new(span.hi - BytePos(unit_len), span.hi),
2227 value: unit,
2228 raw: Some(raw_unit),
2229 },
2230 })
2231 }
2232 _ => {
2233 unreachable!()
2234 }
2235 }
2236 }
2237}
2238
2239impl<I> Parse<Flex> for Parser<I>
2240where
2241 I: ParserInput,
2242{
2243 fn parse(&mut self) -> PResult<Flex> {
2244 let span = self.input.cur_span();
2245
2246 if !is!(self, Dimension) {
2247 return Err(Error::new(span, ErrorKind::Expected("dimension token")));
2248 }
2249
2250 match bump!(self) {
2251 Token::Dimension(dimension_token) => {
2252 let DimensionToken {
2253 value,
2254 unit,
2255 raw_value,
2256 raw_unit,
2257 ..
2258 } = *dimension_token;
2259
2260 if !is_flex_unit(&unit) {
2261 return Err(Error::new(span, ErrorKind::Expected("'fr' unit")));
2262 }
2263
2264 let unit_len = raw_unit.len() as u32;
2265
2266 Ok(Flex {
2267 span,
2268 value: Number {
2269 span: Span::new(span.lo, span.hi - BytePos(unit_len)),
2270 value,
2271 raw: Some(raw_value),
2272 },
2273 unit: Ident {
2274 span: Span::new(span.hi - BytePos(unit_len), span.hi),
2275 value: unit,
2276 raw: Some(raw_unit),
2277 },
2278 })
2279 }
2280 _ => {
2281 unreachable!()
2282 }
2283 }
2284 }
2285}
2286
2287impl<I> Parse<UnknownDimension> for Parser<I>
2288where
2289 I: ParserInput,
2290{
2291 fn parse(&mut self) -> PResult<UnknownDimension> {
2292 let span = self.input.cur_span();
2293
2294 if !is!(self, Dimension) {
2295 return Err(Error::new(span, ErrorKind::Expected("dimension token")));
2296 }
2297
2298 match bump!(self) {
2299 Token::Dimension(dimension_token) => {
2300 let DimensionToken {
2301 value,
2302 unit,
2303 raw_value,
2304 raw_unit,
2305 ..
2306 } = *dimension_token;
2307
2308 let unit_len = raw_unit.len() as u32;
2309
2310 Ok(UnknownDimension {
2311 span,
2312 value: Number {
2313 span: Span::new(span.lo, span.hi - BytePos(unit_len)),
2314 value,
2315 raw: Some(raw_value),
2316 },
2317 unit: Ident {
2318 span: Span::new(span.hi - BytePos(unit_len), span.hi),
2319 value: self.input.atom(unit.to_lowercase()),
2320 raw: Some(raw_unit),
2321 },
2322 })
2323 }
2324 _ => {
2325 unreachable!()
2326 }
2327 }
2328 }
2329}
2330
2331impl<I> Parse<Color> for Parser<I>
2332where
2333 I: ParserInput,
2334{
2335 fn parse(&mut self) -> PResult<Color> {
2336 let span = self.input.cur_span();
2337
2338 match cur!(self) {
2339 Token::Ident { value, .. }
2341 if value.as_ref().eq_ignore_ascii_case("currentcolor")
2342 || is_system_color(value) =>
2343 {
2344 Ok(Color::CurrentColorOrSystemColor(self.parse()?))
2345 }
2346 Token::Function { value, .. } if value.as_ref().eq_ignore_ascii_case("device-cmyk") => {
2348 Ok(Color::Function(self.parse()?))
2349 }
2350 _ => match self.parse() {
2352 Ok(absolute_color_base) => Ok(Color::AbsoluteColorBase(absolute_color_base)),
2353 Err(_) => {
2354 return Err(Error::new(
2355 span,
2356 ErrorKind::Expected(
2357 "hash, ident (with named color, system color, 'transparent' or \
2358 'currentColor' value) or function (with color function name) token",
2359 ),
2360 ));
2361 }
2362 },
2363 }
2364 }
2365}
2366
2367impl<I> Parse<AbsoluteColorBase> for Parser<I>
2368where
2369 I: ParserInput,
2370{
2371 fn parse(&mut self) -> PResult<AbsoluteColorBase> {
2372 let span = self.input.cur_span();
2373
2374 match cur!(self) {
2375 tok!("#") => Ok(AbsoluteColorBase::HexColor(self.parse()?)),
2376 Token::Ident { value, .. } => {
2377 if !(is_named_color(value) || value.as_ref().eq_ignore_ascii_case("transparent")) {
2378 let span = self.input.cur_span();
2379
2380 return Err(Error::new(
2381 span,
2382 ErrorKind::Expected("known named color or 'transparent' keyword"),
2383 ));
2384 }
2385
2386 Ok(AbsoluteColorBase::NamedColorOrTransparent(self.parse()?))
2387 }
2388 Token::Function { value, .. } if is_absolute_color_base_function(value) => {
2389 Ok(AbsoluteColorBase::Function(self.parse()?))
2390 }
2391 _ => {
2392 return Err(Error::new(
2393 span,
2394 ErrorKind::Expected(
2395 "hash, ident (with named color or 'transparent' value) or function (with \
2396 color function name) token",
2397 ),
2398 ));
2399 }
2400 }
2401 }
2402}
2403
2404impl<I> Parse<HexColor> for Parser<I>
2405where
2406 I: ParserInput,
2407{
2408 fn parse(&mut self) -> PResult<HexColor> {
2409 let span = self.input.cur_span();
2410
2411 if !is!(self, "#") {
2412 return Err(Error::new(span, ErrorKind::Expected("hash token")));
2413 }
2414
2415 match bump!(self) {
2416 Token::Hash { value, raw, .. } => {
2417 if value.chars().any(|x| !x.is_ascii_hexdigit()) {
2418 return Err(Error::new(
2419 span,
2420 ErrorKind::Unexpected("character in hex color"),
2421 ));
2422 }
2423
2424 Ok(HexColor {
2425 span,
2426 value,
2427 raw: Some(raw),
2428 })
2429 }
2430 _ => {
2431 unreachable!()
2432 }
2433 }
2434 }
2435}
2436
2437impl<I> Parse<AlphaValue> for Parser<I>
2438where
2439 I: ParserInput,
2440{
2441 fn parse(&mut self) -> PResult<AlphaValue> {
2442 if !is_one_of!(self, "percentage", "number") {
2443 let span = self.input.cur_span();
2444
2445 return Err(Error::new(
2446 span,
2447 ErrorKind::Expected("percentage or number token"),
2448 ));
2449 }
2450
2451 match cur!(self) {
2452 tok!("percentage") => Ok(AlphaValue::Percentage(self.parse()?)),
2453 tok!("number") => Ok(AlphaValue::Number(self.parse()?)),
2454 _ => {
2455 unreachable!()
2456 }
2457 }
2458 }
2459}
2460
2461impl<I> Parse<Hue> for Parser<I>
2462where
2463 I: ParserInput,
2464{
2465 fn parse(&mut self) -> PResult<Hue> {
2466 if !is_one_of!(self, "number", "dimension") {
2467 let span = self.input.cur_span();
2468
2469 return Err(Error::new(
2470 span,
2471 ErrorKind::Expected("number or dimension token"),
2472 ));
2473 }
2474
2475 match cur!(self) {
2476 tok!("number") => Ok(Hue::Number(self.parse()?)),
2477 tok!("dimension") => Ok(Hue::Angle(self.parse()?)),
2478 _ => {
2479 unreachable!()
2480 }
2481 }
2482 }
2483}
2484
2485impl<I> Parse<CmykComponent> for Parser<I>
2486where
2487 I: ParserInput,
2488{
2489 fn parse(&mut self) -> PResult<CmykComponent> {
2490 if !is_one_of!(self, "number", "percentage", "function") {
2491 let span = self.input.cur_span();
2492
2493 return Err(Error::new(
2494 span,
2495 ErrorKind::Expected("number, function or percentage token"),
2496 ));
2497 }
2498
2499 match cur!(self) {
2500 tok!("number") => Ok(CmykComponent::Number(self.parse()?)),
2501 tok!("percentage") => Ok(CmykComponent::Percentage(self.parse()?)),
2502 Token::Function { value, .. } => {
2503 if !is_math_function(value) {
2504 let span = self.input.cur_span();
2505
2506 return Err(Error::new(span, ErrorKind::Expected("math function token")));
2507 }
2508
2509 Ok(CmykComponent::Function(self.parse()?))
2510 }
2511 _ => {
2512 unreachable!()
2513 }
2514 }
2515 }
2516}
2517
2518impl<I> Parse<Percentage> for Parser<I>
2519where
2520 I: ParserInput,
2521{
2522 fn parse(&mut self) -> PResult<Percentage> {
2523 let span = self.input.cur_span();
2524
2525 if !is!(self, Percentage) {
2526 return Err(Error::new(span, ErrorKind::Expected("percentage token")));
2527 }
2528
2529 match bump!(self) {
2530 Token::Percentage { value, raw } => {
2531 let value = Number {
2532 span: Span::new(span.lo, span.hi - BytePos(1)),
2533 value,
2534 raw: Some(raw),
2535 };
2536
2537 Ok(Percentage { span, value })
2538 }
2539 _ => {
2540 unreachable!()
2541 }
2542 }
2543 }
2544}
2545
2546impl<I> Parse<Str> for Parser<I>
2547where
2548 I: ParserInput,
2549{
2550 fn parse(&mut self) -> PResult<Str> {
2551 let span = self.input.cur_span();
2552
2553 if !is!(self, "string") {
2554 return Err(Error::new(span, ErrorKind::Expected("string token")));
2555 }
2556
2557 match bump!(self) {
2558 Token::String { value, raw } => Ok(Str {
2559 span,
2560 value,
2561 raw: Some(raw),
2562 }),
2563 _ => {
2564 unreachable!()
2565 }
2566 }
2567 }
2568}
2569
2570impl<I> Parse<Url> for Parser<I>
2571where
2572 I: ParserInput,
2573{
2574 fn parse(&mut self) -> PResult<Url> {
2575 let span = self.input.cur_span();
2576
2577 if !is_one_of!(self, Url, Function) {
2578 return Err(Error::new(
2579 span,
2580 ErrorKind::Expected("url or function (with 'url' or 'src' name) token"),
2581 ));
2582 }
2583
2584 match bump!(self) {
2585 Token::Url { value, raw } => {
2586 let name_length = raw.0.len() as u32;
2587 let name = Ident {
2588 span: Span::new(span.lo, span.lo + BytePos(name_length)),
2589 value: self.input.atom("url"),
2590 raw: Some(raw.0),
2591 };
2592 let value = Some(Box::new(UrlValue::Raw(UrlValueRaw {
2593 span: Span::new(span.lo + BytePos(name_length + 1), span.hi - BytePos(1)),
2594 value,
2595 raw: Some(raw.1),
2596 })));
2597
2598 Ok(Url {
2599 span: span!(self, span.lo),
2600 name,
2601 value,
2602 modifiers: None,
2603 })
2604 }
2605 Token::Function {
2606 value: function_name,
2607 raw: raw_function_name,
2608 } => {
2609 if !matches_eq_ignore_ascii_case!(function_name, "url", "src") {
2610 return Err(Error::new(
2611 span,
2612 ErrorKind::Expected("'url' or 'src' name of a function token"),
2613 ));
2614 }
2615
2616 let name = Ident {
2617 span: Span::new(span.lo, span.hi - BytePos(1)),
2618 value: function_name,
2619 raw: Some(raw_function_name),
2620 };
2621
2622 self.input.skip_ws();
2623
2624 let value = match cur!(self) {
2625 tok!("string") => Some(Box::new(UrlValue::Str(self.parse()?))),
2626 _ => None,
2627 };
2628
2629 self.input.skip_ws();
2630
2631 let mut modifiers = Vec::new();
2632
2633 loop {
2634 if is!(self, ")") {
2635 break;
2636 }
2637
2638 match cur!(self) {
2639 tok!("ident") => {
2640 modifiers.push(UrlModifier::Ident(self.parse()?));
2641 }
2642 tok!("function") => {
2643 modifiers.push(UrlModifier::Function(self.parse()?));
2644 }
2645 _ => {
2646 let span = self.input.cur_span();
2647
2648 return Err(Error::new(span, ErrorKind::Expected("ident or function")));
2649 }
2650 }
2651
2652 self.input.skip_ws();
2653 }
2654
2655 expect!(self, ")");
2656
2657 Ok(Url {
2658 span: span!(self, span.lo),
2659 name,
2660 value,
2661 modifiers: Some(modifiers),
2662 })
2663 }
2664 _ => {
2665 unreachable!()
2666 }
2667 }
2668 }
2669}
2670
2671impl<I> Parse<UnicodeRange> for Parser<I>
2679where
2680 I: ParserInput,
2681{
2682 fn parse(&mut self) -> PResult<UnicodeRange> {
2683 let span = self.input.cur_span();
2684 let mut unicode_range = String::new();
2685
2686 match cur!(self) {
2688 Token::Ident { value, .. } if matches_eq_ignore_ascii_case!(value, "u") => {
2689 let u = match bump!(self) {
2690 Token::Ident { value, .. } => value,
2691 _ => {
2692 unreachable!();
2693 }
2694 };
2695
2696 unicode_range.push_str(&u);
2697 }
2698 _ => {
2699 return Err(Error::new(span, ErrorKind::Expected("'u' ident token")));
2700 }
2701 };
2702
2703 match cur!(self) {
2704 Token::Delim { value } if *value == '+' => {
2707 let plus = match bump!(self) {
2708 Token::Delim { value } => value,
2709 _ => {
2710 unreachable!();
2711 }
2712 };
2713
2714 unicode_range.push(plus);
2715
2716 if is!(self, Ident) {
2717 let ident = match bump!(self) {
2718 Token::Ident { value, .. } => value,
2719 _ => {
2720 unreachable!();
2721 }
2722 };
2723
2724 unicode_range.push_str(&ident);
2725
2726 loop {
2727 if !is!(self, "?") {
2728 break;
2729 }
2730
2731 let question = match bump!(self) {
2732 Token::Delim { value } => value,
2733 _ => {
2734 unreachable!();
2735 }
2736 };
2737
2738 unicode_range.push(question);
2739 }
2740 } else {
2741 let question = match bump!(self) {
2742 Token::Delim { value } if value == '?' => value,
2743 _ => {
2744 return Err(Error::new(span, ErrorKind::Expected("'?' delim token")));
2745 }
2746 };
2747
2748 unicode_range.push(question);
2749
2750 loop {
2751 if !is!(self, "?") {
2752 break;
2753 }
2754
2755 let question = match bump!(self) {
2756 Token::Delim { value } => value,
2757 _ => {
2758 unreachable!();
2759 }
2760 };
2761
2762 unicode_range.push(question);
2763 }
2764 }
2765 }
2766 tok!("number") => {
2770 let number = match bump!(self) {
2771 Token::Number { raw, .. } => raw,
2772 _ => {
2773 unreachable!();
2774 }
2775 };
2776
2777 unicode_range.push_str(&number);
2778
2779 if !is!(self, EOF) {
2780 match cur!(self) {
2781 tok!("?") => {
2782 let question = match bump!(self) {
2783 Token::Delim { value } => value,
2784 _ => {
2785 unreachable!();
2786 }
2787 };
2788
2789 unicode_range.push(question);
2790
2791 loop {
2792 if !is!(self, "?") {
2793 break;
2794 }
2795
2796 let question = match bump!(self) {
2797 Token::Delim { value } => value,
2798 _ => {
2799 unreachable!();
2800 }
2801 };
2802
2803 unicode_range.push(question);
2804 }
2805 }
2806 tok!("dimension") => {
2807 let raw = match bump!(self) {
2808 Token::Dimension(dimension_token) => {
2809 (dimension_token.raw_value, dimension_token.raw_unit)
2810 }
2811 _ => {
2812 unreachable!();
2813 }
2814 };
2815
2816 unicode_range.push_str(&raw.0);
2817 unicode_range.push_str(&raw.1);
2818 }
2819 tok!("number") => {
2820 let number = match bump!(self) {
2821 Token::Number { raw, .. } => raw,
2822 _ => {
2823 unreachable!();
2824 }
2825 };
2826
2827 unicode_range.push_str(&number);
2828 }
2829 _ => {}
2830 }
2831 }
2832 }
2833 tok!("dimension") => {
2835 let raw = match bump!(self) {
2836 Token::Dimension(dimension_token) => {
2837 (dimension_token.raw_value, dimension_token.raw_unit)
2838 }
2839 _ => {
2840 unreachable!();
2841 }
2842 };
2843
2844 unicode_range.push_str(&raw.0);
2845 unicode_range.push_str(&raw.1);
2846
2847 loop {
2848 if !is!(self, "?") {
2849 break;
2850 }
2851
2852 let question = match bump!(self) {
2853 Token::Delim { value } => value,
2854 _ => {
2855 unreachable!();
2856 }
2857 };
2858
2859 unicode_range.push(question);
2860 }
2861 }
2862 _ => {
2863 return Err(Error::new(
2864 span,
2865 ErrorKind::Expected("dimension, number or '?' delim token"),
2866 ));
2867 }
2868 }
2869
2870 let mut chars = unicode_range.chars();
2871
2872 chars.next();
2875
2876 let mut next = chars.next();
2879
2880 if next != Some('+') {
2881 return Err(Error::new(
2882 span,
2883 ErrorKind::Expected("'+' character after 'u' in unicode range"),
2884 ));
2885 } else {
2886 next = chars.next();
2887 }
2888
2889 let mut start = String::new();
2894
2895 loop {
2896 match next {
2897 Some(c) if c.is_ascii_digit() => {
2898 start.push(c);
2899
2900 next = chars.next();
2901 }
2902 Some(c @ 'A'..='F') | Some(c @ 'a'..='f') => {
2903 start.push(c);
2904
2905 next = chars.next();
2906 }
2907 _ => {
2908 break;
2909 }
2910 }
2911 }
2912
2913 let mut has_question_mark = false;
2914
2915 while let Some(c @ '?') = next {
2916 has_question_mark = true;
2917
2918 start.push(c);
2919
2920 next = chars.next();
2921 }
2922
2923 let len = start.len();
2924
2925 if len == 0 || len > 6 {
2926 return Err(Error::new(
2927 span,
2928 ErrorKind::Expected(
2929 "valid length (minimum 1 or maximum 6 hex digits) in the start of unicode \
2930 range",
2931 ),
2932 ));
2933 }
2934
2935 if has_question_mark {
2937 if next.is_some() {
2940 return Err(Error::new(
2941 span,
2942 ErrorKind::Expected("no characters after '?' in unicode range"),
2943 ));
2944 }
2945
2946 return Ok(UnicodeRange {
2957 span: span!(self, span.lo),
2958 start: self.input.atom(start),
2959 end: None,
2960 raw: Some(self.input.atom(unicode_range)),
2961 });
2962 }
2963
2964 if next.is_none() {
2970 return Ok(UnicodeRange {
2971 span: span!(self, span.lo),
2972 start: self.input.atom(start),
2973 end: None,
2974 raw: Some(self.input.atom(unicode_range)),
2975 });
2976 }
2977
2978 if next != Some('-') {
2981 return Err(Error::new(
2982 span,
2983 ErrorKind::Expected("'-' between start and end in unicode range"),
2984 ));
2985 } else {
2986 next = chars.next();
2987 }
2988
2989 let mut end = String::new();
2995
2996 loop {
2997 match next {
2998 Some(c) if c.is_ascii_digit() => {
2999 end.push(c);
3000 next = chars.next();
3001 }
3002 Some(c @ 'A'..='F') | Some(c @ 'a'..='f') => {
3003 end.push(c);
3004 next = chars.next();
3005 }
3006 _ => {
3007 break;
3008 }
3009 }
3010 }
3011
3012 let len = end.len();
3013
3014 if len == 0 || len > 6 {
3015 return Err(Error::new(
3016 span,
3017 ErrorKind::Expected(
3018 "valid length (minimum 1 or maximum 6 hex digits) in the end of unicode range",
3019 ),
3020 ));
3021 }
3022
3023 if chars.next().is_some() {
3024 return Err(Error::new(
3025 span,
3026 ErrorKind::Expected("no characters after end in unicode range"),
3027 ));
3028 }
3029
3030 return Ok(UnicodeRange {
3031 span: span!(self, span.lo),
3032 start: self.input.atom(start),
3033 end: Some(self.input.atom(end)),
3034 raw: Some(self.input.atom(unicode_range)),
3035 });
3036 }
3037}
3038
3039impl<I> Parse<CalcSum> for Parser<I>
3040where
3041 I: ParserInput,
3042{
3043 fn parse(&mut self) -> PResult<CalcSum> {
3044 let start = self.input.cur_span().lo;
3045 let mut expressions = Vec::new();
3046 let calc_product = CalcProductOrOperator::Product(self.parse()?);
3047 let mut end = match calc_product {
3048 CalcProductOrOperator::Product(ref calc_product) => calc_product.span.hi,
3049 _ => {
3050 unreachable!();
3051 }
3052 };
3053
3054 expressions.push(calc_product);
3055
3056 loop {
3057 self.input.skip_ws();
3058
3059 if is!(self, EOF) {
3060 break;
3061 }
3062
3063 match cur!(self) {
3064 tok!("+") | tok!("-") => {
3065 let operator = CalcProductOrOperator::Operator(self.parse()?);
3066
3067 expressions.push(operator);
3068
3069 self.input.skip_ws();
3070
3071 let calc_product = CalcProductOrOperator::Product(self.parse()?);
3072
3073 end = match calc_product {
3074 CalcProductOrOperator::Product(ref calc_product) => calc_product.span.hi,
3075 _ => {
3076 unreachable!();
3077 }
3078 };
3079
3080 expressions.push(calc_product);
3081 }
3082 _ => {
3083 break;
3084 }
3085 }
3086 }
3087
3088 Ok(CalcSum {
3089 span: Span::new(start, end),
3090 expressions,
3091 })
3092 }
3093}
3094
3095impl<I> Parse<CalcProduct> for Parser<I>
3096where
3097 I: ParserInput,
3098{
3099 fn parse(&mut self) -> PResult<CalcProduct> {
3100 let start = self.input.cur_span().lo;
3101 let mut expressions = Vec::new();
3102 let calc_value = CalcValueOrOperator::Value(self.parse()?);
3103 let mut end = match calc_value {
3104 CalcValueOrOperator::Value(ref calc_value) => match calc_value {
3105 CalcValue::Number(item) => item.span.hi,
3106 CalcValue::Percentage(item) => item.span.hi,
3107 CalcValue::Constant(item) => item.span.hi,
3108 CalcValue::Sum(item) => item.span.hi,
3109 CalcValue::Function(item) => item.span.hi,
3110 CalcValue::Dimension(item) => match item {
3111 Dimension::Length(item) => item.span.hi,
3112 Dimension::Angle(item) => item.span.hi,
3113 Dimension::Time(item) => item.span.hi,
3114 Dimension::Frequency(item) => item.span.hi,
3115 Dimension::Resolution(item) => item.span.hi,
3116 Dimension::Flex(item) => item.span.hi,
3117 Dimension::UnknownDimension(item) => item.span.hi,
3118 },
3119 },
3120 _ => {
3121 unreachable!();
3122 }
3123 };
3124
3125 expressions.push(calc_value);
3126
3127 loop {
3128 self.input.skip_ws();
3129
3130 if is!(self, EOF) {
3131 break;
3132 }
3133
3134 match cur!(self) {
3135 tok!("*") | tok!("/") => {
3136 let operator = CalcValueOrOperator::Operator(self.parse()?);
3137
3138 expressions.push(operator);
3139
3140 self.input.skip_ws();
3141
3142 let calc_value = CalcValueOrOperator::Value(self.parse()?);
3143
3144 end = match calc_value {
3145 CalcValueOrOperator::Value(ref calc_value) => match calc_value {
3146 CalcValue::Number(item) => item.span.hi,
3147 CalcValue::Percentage(item) => item.span.hi,
3148 CalcValue::Constant(item) => item.span.hi,
3149 CalcValue::Sum(item) => item.span.hi,
3150 CalcValue::Function(item) => item.span.hi,
3151 CalcValue::Dimension(item) => match item {
3152 Dimension::Length(item) => item.span.hi,
3153 Dimension::Angle(item) => item.span.hi,
3154 Dimension::Time(item) => item.span.hi,
3155 Dimension::Frequency(item) => item.span.hi,
3156 Dimension::Resolution(item) => item.span.hi,
3157 Dimension::Flex(item) => item.span.hi,
3158 Dimension::UnknownDimension(item) => item.span.hi,
3159 },
3160 },
3161 _ => {
3162 unreachable!();
3163 }
3164 };
3165
3166 expressions.push(calc_value);
3167 }
3168 _ => {
3169 break;
3170 }
3171 }
3172 }
3173
3174 Ok(CalcProduct {
3175 span: Span::new(start, end),
3176 expressions,
3177 })
3178 }
3179}
3180
3181impl<I> Parse<CalcOperator> for Parser<I>
3182where
3183 I: ParserInput,
3184{
3185 fn parse(&mut self) -> PResult<CalcOperator> {
3186 let span = self.input.cur_span();
3187
3188 match cur!(self) {
3189 tok!("+") => {
3190 bump!(self);
3191
3192 Ok(CalcOperator {
3193 span: span!(self, span.lo),
3194 value: CalcOperatorType::Add,
3195 })
3196 }
3197 tok!("-") => {
3198 bump!(self);
3199
3200 Ok(CalcOperator {
3201 span: span!(self, span.lo),
3202 value: CalcOperatorType::Sub,
3203 })
3204 }
3205 tok!("*") => {
3206 bump!(self);
3207
3208 Ok(CalcOperator {
3209 span: span!(self, span.lo),
3210 value: CalcOperatorType::Mul,
3211 })
3212 }
3213 tok!("/") => {
3214 bump!(self);
3215
3216 Ok(CalcOperator {
3217 span: span!(self, span.lo),
3218 value: CalcOperatorType::Div,
3219 })
3220 }
3221 _ => {
3222 let span = self.input.cur_span();
3223
3224 return Err(Error::new(
3225 span,
3226 ErrorKind::Expected("'+', '-', '*' or '/' delim tokens"),
3227 ));
3228 }
3229 }
3230 }
3231}
3232
3233impl<I> Parse<CalcValue> for Parser<I>
3234where
3235 I: ParserInput,
3236{
3237 fn parse(&mut self) -> PResult<CalcValue> {
3238 match cur!(self) {
3239 tok!("number") => Ok(CalcValue::Number(self.parse()?)),
3240 tok!("dimension") => Ok(CalcValue::Dimension(self.parse()?)),
3241 tok!("percentage") => Ok(CalcValue::Percentage(self.parse()?)),
3242 Token::Ident { value, .. } => {
3243 match &*value.to_ascii_lowercase() {
3244 "e" | "pi" | "infinity" | "-infinity" | "nan" => {}
3245 _ => {
3246 let span = self.input.cur_span();
3247
3248 return Err(Error::new(
3249 span,
3250 ErrorKind::Expected(
3251 "'e', 'pi', 'infinity', '-infinity' or 'NaN', ident tokens",
3252 ),
3253 ));
3254 }
3255 }
3256
3257 Ok(CalcValue::Constant(self.parse()?))
3258 }
3259 tok!("(") => {
3260 let span = self.input.cur_span();
3261
3262 expect!(self, "(");
3263
3264 self.input.skip_ws();
3265
3266 let mut calc_sum_in_parens: CalcSum = self.parse()?;
3267
3268 self.input.skip_ws();
3269
3270 expect!(self, ")");
3271
3272 calc_sum_in_parens.span = span!(self, span.lo);
3273
3274 Ok(CalcValue::Sum(calc_sum_in_parens))
3275 }
3276 tok!("function") => Ok(CalcValue::Function(self.parse()?)),
3277 _ => {
3278 let span = self.input.cur_span();
3279
3280 return Err(Error::new(
3281 span,
3282 ErrorKind::Expected(
3283 "'number', 'dimension', 'percentage', 'ident', '(' or 'function' tokens",
3284 ),
3285 ));
3286 }
3287 }
3288 }
3289}
3290
3291impl<I> Parse<FamilyName> for Parser<I>
3292where
3293 I: ParserInput,
3294{
3295 fn parse(&mut self) -> PResult<FamilyName> {
3296 match cur!(self) {
3297 tok!("string") => Ok(FamilyName::Str(self.parse()?)),
3298 tok!("ident") => {
3299 let span = self.input.cur_span();
3300
3301 let mut value = vec![self.parse()?];
3302
3303 loop {
3304 self.input.skip_ws();
3305
3306 if !is!(self, "ident") {
3307 break;
3308 }
3309
3310 value.push(self.parse()?);
3311 }
3312
3313 Ok(FamilyName::SequenceOfCustomIdents(SequenceOfCustomIdents {
3314 span: span!(self, span.lo),
3315 value,
3316 }))
3317 }
3318 _ => {
3319 let span = self.input.cur_span();
3320
3321 return Err(Error::new(
3322 span,
3323 ErrorKind::Expected("'string' or 'ident' tokens"),
3324 ));
3325 }
3326 }
3327 }
3328}
3329
3330pub(crate) fn is_math_function(name: &Atom) -> bool {
3331 matches_eq_ignore_ascii_case!(
3332 name,
3333 "calc",
3334 "-moz-calc",
3335 "-webkit-calc",
3336 "sin",
3337 "cos",
3338 "tan",
3339 "asin",
3340 "acos",
3341 "atan",
3342 "sqrt",
3343 "exp",
3344 "abs",
3345 "sign",
3346 "min",
3347 "max",
3348 "hypot",
3349 "clamp",
3350 "round",
3351 "mod",
3352 "rem",
3353 "atan2",
3354 "pow",
3355 "log"
3356 )
3357}
3358
3359fn is_absolute_color_base_function(name: &Atom) -> bool {
3360 matches_eq_ignore_ascii_case!(
3361 name,
3362 "rgb",
3363 "rgba",
3364 "hsl",
3365 "hsla",
3366 "hwb",
3367 "lab",
3368 "lch",
3369 "oklab",
3370 "oklch",
3371 "color",
3372 "color-mix",
3373 "color-contrast"
3374 )
3375}
3376
3377fn is_system_color(name: &Atom) -> bool {
3378 matches_eq_ignore_ascii_case!(
3379 name,
3380 "canvas",
3381 "canvastext",
3382 "linktext",
3383 "visitedtext",
3384 "activetext",
3385 "buttonface",
3386 "buttontext",
3387 "buttonborder",
3388 "field",
3389 "fieldtext",
3390 "highlight",
3391 "highlighttext",
3392 "selecteditem",
3393 "selecteditemtext",
3394 "mark",
3395 "marktext",
3396 "graytext",
3397 "activeborder",
3399 "activecaption",
3400 "appWorkspace",
3401 "background",
3402 "buttonhighlight",
3403 "buttonshadow",
3404 "captiontext",
3405 "inactiveborder",
3406 "inactivecaption",
3407 "inactivecaptiontext",
3408 "infobackground",
3409 "infotext",
3410 "menu",
3411 "menutext",
3412 "scrollbar",
3413 "threeddarkshadow",
3414 "threedface",
3415 "threedhighlight",
3416 "threedlightshadow",
3417 "threedshadow",
3418 "window",
3419 "windowframe",
3420 "windowtext",
3421 "-moz-buttondefault",
3423 "-moz-buttonhoverface",
3424 "-moz-buttonhovertext",
3425 "-moz-cellhighlight",
3426 "-moz-cellhighlighttext",
3427 "-moz-combobox",
3428 "-moz-comboboxtext",
3429 "-moz-dialog",
3430 "-moz-dialogtext",
3431 "-moz-dragtargetzone",
3432 "-moz-eventreerow",
3433 "-moz-html-cellhighlight",
3434 "-moz-html-cellhighlighttext",
3435 "-moz-mac-accentdarkestshadow",
3436 "-moz-mac-accentdarkshadow",
3437 "-moz-mac-accentface",
3438 "-moz-mac-accentlightesthighlight",
3439 "-moz-mac-accentlightshadow",
3440 "-moz-mac-accentregularhighlight",
3441 "-moz-mac-accentregularshadow",
3442 "-moz-mac-chrome-active",
3443 "-moz-mac-chrome-inactive",
3444 "-moz-mac-focusring",
3445 "-moz-mac-menuselect",
3446 "-moz-mac-menushadow",
3447 "-moz-mac-menutextselect",
3448 "-moz-menuhover",
3449 "-moz-menuhovertext",
3450 "-moz-menubartext",
3451 "-moz-menubarhovertext",
3452 "-moz-nativehyperlinktext",
3453 "-moz-oddtreerow",
3454 "-moz-win-communicationstext",
3455 "-moz-win-mediatext",
3456 "-moz-win-accentcolor",
3457 "-moz-win-accentcolortext",
3458 "-moz-activehyperlinktext",
3460 "-moz-default-background-color",
3461 "-moz-default-color",
3462 "-moz-hyperlinktext",
3463 "-moz-visitedhyperlinktext"
3464 )
3465}
3466
3467fn is_named_color(name: &Atom) -> bool {
3468 matches_eq_ignore_ascii_case!(
3469 name,
3470 "aliceblue",
3471 "antiquewhite",
3472 "aqua",
3473 "aquamarine",
3474 "azure",
3475 "beige",
3476 "bisque",
3477 "black",
3478 "blanchedalmond",
3479 "blue",
3480 "blueviolet",
3481 "brown",
3482 "burlywood",
3483 "cadetblue",
3484 "chartreuse",
3485 "chocolate",
3486 "coral",
3487 "cornflowerblue",
3488 "cornsilk",
3489 "crimson",
3490 "cyan",
3491 "darkblue",
3492 "darkcyan",
3493 "darkgoldenrod",
3494 "darkgray",
3495 "darkgreen",
3496 "darkgrey",
3497 "darkkhaki",
3498 "darkmagenta",
3499 "darkolivegreen",
3500 "darkorange",
3501 "darkorchid",
3502 "darkred",
3503 "darksalmon",
3504 "darkseagreen",
3505 "darkslateblue",
3506 "darkslategray",
3507 "darkslategrey",
3508 "darkturquoise",
3509 "darkviolet",
3510 "deeppink",
3511 "deepskyblue",
3512 "dimgray",
3513 "dimgrey",
3514 "dodgerblue",
3515 "firebrick",
3516 "floralwhite",
3517 "forestgreen",
3518 "fuchsia",
3519 "gainsboro",
3520 "ghostwhite",
3521 "gold",
3522 "goldenrod",
3523 "gray",
3524 "green",
3525 "greenyellow",
3526 "grey",
3527 "honeydew",
3528 "hotpink",
3529 "indianred",
3530 "indigo",
3531 "ivory",
3532 "khaki",
3533 "lavender",
3534 "lavenderblush",
3535 "lawngreen",
3536 "lemonchiffon",
3537 "lightblue",
3538 "lightcoral",
3539 "lightcyan",
3540 "lightgoldenrodyellow",
3541 "lightgray",
3542 "lightgreen",
3543 "lightgrey",
3544 "lightpink",
3545 "lightsalmon",
3546 "lightseagreen",
3547 "lightskyblue",
3548 "lightslategray",
3549 "lightslategrey",
3550 "lightsteelblue",
3551 "lightyellow",
3552 "lime",
3553 "limegreen",
3554 "linen",
3555 "magenta",
3556 "maroon",
3557 "mediumaquamarine",
3558 "mediumblue",
3559 "mediumorchid",
3560 "mediumpurple",
3561 "mediumseagreen",
3562 "mediumslateblue",
3563 "mediumspringgreen",
3564 "mediumturquoise",
3565 "mediumvioletred",
3566 "midnightblue",
3567 "mintcream",
3568 "mistyrose",
3569 "moccasin",
3570 "navajowhite",
3571 "navy",
3572 "oldlace",
3573 "olive",
3574 "olivedrab",
3575 "orange",
3576 "orangered",
3577 "orchid",
3578 "palegoldenrod",
3579 "palegreen",
3580 "paleturquoise",
3581 "palevioletred",
3582 "papayawhip",
3583 "peachpuff",
3584 "peru",
3585 "pink",
3586 "plum",
3587 "powderblue",
3588 "purple",
3589 "rebeccapurple",
3590 "red",
3591 "rosybrown",
3592 "royalblue",
3593 "saddlebrown",
3594 "salmon",
3595 "sandybrown",
3596 "seagreen",
3597 "seashell",
3598 "sienna",
3599 "silver",
3600 "skyblue",
3601 "slateblue",
3602 "slategray",
3603 "slategrey",
3604 "snow",
3605 "springgreen",
3606 "steelblue",
3607 "tan",
3608 "teal",
3609 "thistle",
3610 "tomato",
3611 "turquoise",
3612 "violet",
3613 "wheat",
3614 "white",
3615 "whitesmoke",
3616 "yellow",
3617 "yellowgreen"
3618 )
3619}
3620
3621fn is_length_unit(unit: &Atom) -> bool {
3622 matches_eq_ignore_ascii_case!(
3623 unit, "em", "rem", "ex", "rex", "cap", "rcap", "ch", "rch", "ic", "ric", "lh", "rlh",
3624 "vw", "svw", "lvw", "dvw", "vh", "svh", "lvh", "dvh", "vi", "svi", "lvi", "dvi", "vb",
3626 "svb", "lvb", "dvb", "vmin", "svmin", "lvmin", "dvmin", "vmax", "svmax", "lvmax", "dvmax",
3627 "cm", "mm", "q", "in", "pc", "pt", "px", "mozmm"
3629 )
3630}
3631
3632fn is_container_lengths_unit(unit: &Atom) -> bool {
3633 matches_eq_ignore_ascii_case!(unit, "cqw", "cqh", "cqi", "cqb", "cqmin", "cqmax")
3634}
3635
3636fn is_angle_unit(unit: &Atom) -> bool {
3637 matches_eq_ignore_ascii_case!(unit, "deg", "grad", "rad", "turn")
3638}
3639
3640fn is_time_unit(unit: &Atom) -> bool {
3641 matches_eq_ignore_ascii_case!(unit, "s", "ms")
3642}
3643
3644fn is_frequency_unit(unit: &Atom) -> bool {
3645 matches_eq_ignore_ascii_case!(unit, "hz", "khz")
3646}
3647
3648fn is_resolution_unit(unit: &Atom) -> bool {
3649 matches_eq_ignore_ascii_case!(unit, "dpi", "dpcm", "dppx", "x")
3650}
3651
3652fn is_flex_unit(unit: &Atom) -> bool {
3653 matches_eq_ignore_ascii_case!(unit, "fr")
3654}