1macro_rules! enum_property {
2 (
3 $(#[$outer:meta])*
4 $vis:vis enum $name:ident {
5 $(
6 $(#[$meta: meta])*
7 $x: ident,
8 )+
9 }
10 ) => {
11 $(#[$outer])*
12 #[derive(Debug, Clone, Copy, PartialEq)]
13 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(rename_all = "lowercase"))]
14 $vis enum $name {
15 $(
16 $(#[$meta])*
17 $x,
18 )+
19 }
20
21 impl<'i> Parse<'i> for $name {
22 fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
23 let location = input.current_source_location();
24 let ident = input.expect_ident()?;
25 match &ident[..] {
26 $(
27 s if s.eq_ignore_ascii_case(stringify!($x)) => Ok($name::$x),
28 )+
29 _ => Err(location.new_unexpected_token_error(
30 cssparser::Token::Ident(ident.clone())
31 ))
32 }
33 }
34
35 fn parse_string(input: &'i str) -> Result<Self, ParseError<'i, ParserError<'i>>> {
36 match input {
37 $(
38 s if s.eq_ignore_ascii_case(stringify!($x)) => Ok($name::$x),
39 )+
40 _ => return Err(ParseError {
41 kind: ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(cssparser::Token::Ident(input.into()))),
42 location: cssparser::SourceLocation { line: 0, column: 1 }
43 })
44 }
45 }
46 }
47
48 impl ToCss for $name {
49 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError> where W: std::fmt::Write {
50 use $name::*;
51 match self {
52 $(
53 $x => dest.write_str(&stringify!($x).to_lowercase()),
54 )+
55 }
56 }
57 }
58 };
59 (
60 $(#[$outer:meta])*
61 $vis:vis enum $name:ident {
62 $(
63 $(#[$meta: meta])*
64 $str: literal: $id: ident,
65 )+
66 }
67 ) => {
68 $(#[$outer])*
69 #[derive(Debug, Clone, Copy, PartialEq)]
70 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
71 $vis enum $name {
72 $(
73 $(#[$meta])*
74 #[cfg_attr(feature = "serde", serde(rename = $str))]
75 $id,
76 )+
77 }
78
79 impl<'i> Parse<'i> for $name {
80 fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
81 let location = input.current_source_location();
82 let ident = input.expect_ident()?;
83 match &ident[..] {
84 $(
85 s if s.eq_ignore_ascii_case($str) => Ok($name::$id),
86 )+
87 _ => Err(location.new_unexpected_token_error(
88 cssparser::Token::Ident(ident.clone())
89 ))
90 }
91 }
92
93 fn parse_string(input: &'i str) -> Result<Self, ParseError<'i, ParserError<'i>>> {
94 match input {
95 $(
96 s if s.eq_ignore_ascii_case($str) => Ok($name::$id),
97 )+
98 _ => return Err(ParseError {
99 kind: ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(cssparser::Token::Ident(input.into()))),
100 location: cssparser::SourceLocation { line: 0, column: 1 }
101 })
102 }
103 }
104 }
105
106 impl ToCss for $name {
107 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError> where W: std::fmt::Write {
108 use $name::*;
109 match self {
110 $(
111 $id => dest.write_str($str),
112 )+
113 }
114 }
115 }
116 };
117}
118
119pub(crate) use enum_property;
120
121macro_rules! shorthand_property {
122 (
123 $(#[$outer:meta])*
124 $vis:vis struct $name: ident$(<$l: lifetime>)? {
125 $(#[$first_meta: meta])*
126 $first_key: ident: $first_prop: ident($first_type: ty $(, $first_vp: ty)?),
127 $(
128 $(#[$meta: meta])*
129 $key: ident: $prop: ident($type: ty $(, $vp: ty)?),
130 )*
131 }
132 ) => {
133 define_shorthand! {
134 $(#[$outer])*
135 pub struct $name$(<$l>)? {
136 $(#[$first_meta])*
137 $first_key: $first_prop($first_type $($first_vp)?),
138 $(
139 $(#[$meta])*
140 $key: $prop($type $($vp)?),
141 )*
142 }
143 }
144
145 impl<'i> Parse<'i> for $name$(<$l>)? {
146 fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
147 let mut $first_key = None;
148 $(
149 let mut $key = None;
150 )*
151
152 macro_rules! parse_one {
153 ($k: ident, $t: ty) => {
154 if $k.is_none() {
155 if let Ok(val) = input.try_parse(<$t>::parse) {
156 $k = Some(val);
157 continue
158 }
159 }
160 };
161 }
162
163 loop {
164 parse_one!($first_key, $first_type);
165 $(
166 parse_one!($key, $type);
167 )*
168 break
169 }
170
171 Ok($name {
172 $first_key: $first_key.unwrap_or_default(),
173 $(
174 $key: $key.unwrap_or_default(),
175 )*
176 })
177 }
178 }
179
180 impl$(<$l>)? ToCss for $name$(<$l>)? {
181 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError> where W: std::fmt::Write {
182 let mut needs_space = false;
183 macro_rules! print_one {
184 ($k: ident, $t: ty) => {
185 if self.$k != <$t>::default() {
186 if needs_space {
187 dest.write_char(' ')?;
188 }
189 self.$k.to_css(dest)?;
190 needs_space = true;
191 }
192 };
193 }
194
195 print_one!($first_key, $first_type);
196 $(
197 print_one!($key, $type);
198 )*
199 if !needs_space {
200 self.$first_key.to_css(dest)?;
201 }
202 Ok(())
203 }
204 }
205 };
206}
207
208pub(crate) use shorthand_property;
209
210macro_rules! shorthand_handler {
211 (
212 $name: ident -> $shorthand: ident$(<$l: lifetime>)? $(fallbacks: $shorthand_fallback: literal)?
213 { $( $key: ident: $prop: ident($type: ty $(, fallback: $fallback: literal)? $(, image: $image: literal)?), )+ }
214 ) => {
215 #[derive(Default)]
216 pub(crate) struct $name$(<$l>)? {
217 #[allow(dead_code)]
218 targets: Option<Browsers>,
219 $(
220 pub $key: Option<$type>,
221 )*
222 has_any: bool
223 }
224
225 impl$(<$l>)? $name$(<$l>)? {
226 #[allow(dead_code)]
227 pub fn new(targets: Option<Browsers>) -> Self {
228 Self {
229 targets,
230 ..Self::default()
231 }
232 }
233 }
234
235 impl<'i> PropertyHandler<'i> for $name$(<$l>)? {
236 fn handle_property(&mut self, property: &Property<'i>, dest: &mut DeclarationList<'i>, context: &mut PropertyHandlerContext<'i, '_>) -> bool {
237 match property {
238 $(
239 Property::$prop(val) => {
240 $(
241 if $image && val.should_preserve_fallback(&self.$key, self.targets) {
242 self.finalize(dest, context);
243 }
244 )?
245 self.$key = Some(val.clone());
246 self.has_any = true;
247 },
248 )+
249 Property::$shorthand(val) => {
250 $(
251 $(
252 if $image && val.$key.should_preserve_fallback(&self.$key, self.targets) {
253 self.finalize(dest, context);
254 }
255 )?
256
257 self.$key = Some(val.$key.clone());
258 )+
259 self.has_any = true;
260 }
261 Property::Unparsed(val) if matches!(val.property_id, $( PropertyId::$prop | )+ PropertyId::$shorthand) => {
262 self.finalize(dest, context);
263
264 let mut unparsed = val.clone();
265 context.add_unparsed_fallbacks(&mut unparsed);
266 dest.push(Property::Unparsed(unparsed));
267 }
268 _ => return false
269 }
270
271 true
272 }
273
274 fn finalize(&mut self, dest: &mut DeclarationList<'i>, _: &mut PropertyHandlerContext<'i, '_>) {
275 if !self.has_any {
276 return
277 }
278
279 self.has_any = false;
280
281 $(
282 let $key = std::mem::take(&mut self.$key);
283 )+
284
285 if $( $key.is_some() && )* true {
286 #[allow(unused_mut)]
287 let mut shorthand = $shorthand {
288 $(
289 $key: $key.unwrap(),
290 )+
291 };
292
293 $(
294 if $shorthand_fallback {
295 if let Some(targets) = self.targets {
296 let fallbacks = shorthand.get_fallbacks(targets);
297 for fallback in fallbacks {
298 dest.push(Property::$shorthand(fallback));
299 }
300 }
301 }
302 )?
303
304 dest.push(Property::$shorthand(shorthand))
305 } else {
306 $(
307 #[allow(unused_mut)]
308 if let Some(mut val) = $key {
309 $(
310 if $fallback {
311 if let Some(targets) = self.targets {
312 let fallbacks = val.get_fallbacks(targets);
313 for fallback in fallbacks {
314 dest.push(Property::$prop(fallback));
315 }
316 }
317 }
318 )?
319
320 dest.push(Property::$prop(val))
321 }
322 )+
323 }
324 }
325 }
326 };
327}
328
329pub(crate) use shorthand_handler;
330
331macro_rules! define_shorthand {
332 (
333 $(#[$outer:meta])*
334 $vis:vis struct $name: ident$(<$l: lifetime>)?$(($prefix: ty))? {
335 $(
336 $(#[$meta: meta])*
337 $key: ident: $prop: ident($type: ty $(, $vp: ty)?),
338 )+
339 }
340 ) => {
341 $(#[$outer])*
342 #[derive(Debug, Clone, PartialEq)]
343 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
344 pub struct $name$(<$l>)? {
345 $(
346 $(#[$meta])*
347 pub $key: $type,
348 )+
349 }
350
351 crate::macros::impl_shorthand! {
352 $name($name$(<$l>)? $(, $prefix)?) {
353 $(
354 $key: [ $prop$(($vp))?, ],
355 )+
356 }
357 }
358 };
359}
360
361pub(crate) use define_shorthand;
362
363macro_rules! impl_shorthand {
364 (
365 $name: ident($t: ty $(, $prefix: ty)?) {
366 $(
367 $key: ident: [ $( $prop: ident$(($vp: ty))? $(,)?)+ ],
368 )+
369 }
370
371 $(
372 fn is_valid($v: ident) {
373 $($body: tt)+
374 }
375 )?
376 ) => {
377 #[allow(unused_macros)]
378 macro_rules! vp_name {
379 ($x: ty, $n: ident) => {
380 $n
381 };
382 ($x: ty, $n: expr) => {
383 $n
384 };
385 }
386
387 impl<'i> Shorthand<'i> for $t {
388 #[allow(unused_variables)]
389 fn from_longhands(decls: &DeclarationBlock<'i>, vendor_prefix: crate::vendor_prefix::VendorPrefix) -> Option<(Self, bool)> {
390 $(
391 $(
392 #[allow(non_snake_case)]
393 let mut $prop = None;
394 )+
395 )+
396
397 let mut count = 0;
398 let mut important_count = 0;
399 for (property, important) in decls.iter() {
400 match property {
401 $(
402 $(
403 Property::$prop(val $(, vp_name!($vp, p))?) => {
404 $(
405 if *vp_name!($vp, p) != vendor_prefix {
406 return None
407 }
408 )?
409
410 $prop = Some(val.clone());
411 count += 1;
412 if important {
413 important_count += 1;
414 }
415 }
416 )+
417 )+
418 Property::$name(val $(, vp_name!($prefix, p))?) => {
419 $(
420 if *vp_name!($prefix, p) != vendor_prefix {
421 return None
422 }
423 )?
424
425 $(
426 $(
427 $prop = Some(val.$key.clone());
428 count += 1;
429 if important {
430 important_count += 1;
431 }
432 )+
433 )+
434 }
435 _ => {
436 $(
437 $(
438 if let Some(Property::$prop(longhand $(, vp_name!($vp, _p))?)) = property.longhand(&PropertyId::$prop$((vp_name!($vp, vendor_prefix)))?) {
439 $prop = Some(longhand);
440 count += 1;
441 if important {
442 important_count += 1;
443 }
444 }
445 )+
446 )+
447 }
448 }
449 }
450
451 if important_count > 0 && important_count != count {
453 return None
454 }
455
456 if $($($prop.is_some() &&)+)+ true {
457 $(
459 let mut $key = None;
460 $(
461 if $key == None {
462 $key = $prop;
463 } else if $key != $prop {
464 return None
465 }
466 )+
467 )+
468
469 let value = $name {
470 $(
471 $key: $key.unwrap(),
472 )+
473 };
474
475 $(
476 #[inline]
477 fn is_valid($v: &$name) -> bool {
478 $($body)+
479 }
480
481 if !is_valid(&value) {
482 return None
483 }
484 )?
485
486 return Some((value, important_count > 0));
487 }
488
489 None
490 }
491
492 #[allow(unused_variables)]
493 fn longhands(vendor_prefix: crate::vendor_prefix::VendorPrefix) -> Vec<PropertyId<'static>> {
494 vec![$($(PropertyId::$prop$((vp_name!($vp, vendor_prefix)))?, )+)+]
495 }
496
497 fn longhand(&self, property_id: &PropertyId) -> Option<Property<'i>> {
498 match property_id {
499 $(
500 $(
501 PropertyId::$prop$((vp_name!($vp, p)))? => {
502 Some(Property::$prop(self.$key.clone() $(, *vp_name!($vp, p))?))
503 }
504 )+
505 )+
506 _ => None
507 }
508 }
509
510 fn set_longhand(&mut self, property: &Property<'i>) -> Result<(), ()> {
511 macro_rules! count {
512 ($p: ident) => {
513 1
514 }
515 }
516
517 $(
518 #[allow(non_upper_case_globals)]
519 const $key: u8 = 0 $( + count!($prop))+;
520 )+
521
522 match property {
523 $(
524 $(
525 Property::$prop(val $(, vp_name!($vp, _p))?) => {
526 if $key > 1 {
528 return Err(())
529 }
530 self.$key = val.clone();
531 return Ok(())
532 }
533 )+
534 )+
535 _ => {}
536 }
537 Err(())
538 }
539 }
540 }
541}
542
543pub(crate) use impl_shorthand;
544
545macro_rules! define_list_shorthand {
546 (
547 $(#[$outer:meta])*
548 $vis:vis struct $name: ident$(<$l: lifetime>)?$(($prefix: ty))? {
549 $(
550 $(#[$meta: meta])*
551 $key: ident: $prop: ident($type: ty $(, $vp: ty)?),
552 )+
553 }
554 ) => {
555 $(#[$outer])*
556 #[derive(Debug, Clone, PartialEq)]
557 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
558 pub struct $name$(<$l>)? {
559 $(
560 $(#[$meta])*
561 pub $key: $type,
562 )+
563 }
564
565 #[allow(unused_macros)]
566 macro_rules! vp_name {
567 ($x: ty, $n: ident) => {
568 $n
569 };
570 ($x: ty, $n: expr) => {
571 $n
572 };
573 }
574
575 impl<'i> Shorthand<'i> for SmallVec<[$name$(<$l>)?; 1]> {
576 #[allow(unused_variables)]
577 fn from_longhands(decls: &DeclarationBlock<'i>, vendor_prefix: crate::vendor_prefix::VendorPrefix) -> Option<(Self, bool)> {
578 $(
579 let mut $key = None;
580 )+
581
582 let mut count = 0;
583 let mut important_count = 0;
584 let mut length = None;
585 for (property, important) in decls.iter() {
586 let mut len = 0;
587 match property {
588 $(
589 Property::$prop(val $(, vp_name!($vp, p))?) => {
590 $(
591 if *vp_name!($vp, p) != vendor_prefix {
592 return None
593 }
594 )?
595
596 $key = Some(val.clone());
597 len = val.len();
598 count += 1;
599 if important {
600 important_count += 1;
601 }
602 }
603 )+
604 Property::$name(val $(, vp_name!($prefix, p))?) => {
605 $(
606 if *vp_name!($prefix, p) != vendor_prefix {
607 return None
608 }
609 )?
610 $(
611 $key = Some(val.iter().map(|b| b.$key.clone()).collect());
612 )+
613 len = val.len();
614 count += 1;
615 if important {
616 important_count += 1;
617 }
618 }
619 _ => {
620 $(
621 if let Some(Property::$prop(longhand $(, vp_name!($vp, _p))?)) = property.longhand(&PropertyId::$prop$((vp_name!($vp, vendor_prefix)))?) {
622 len = longhand.len();
623 $key = Some(longhand);
624 count += 1;
625 if important {
626 important_count += 1;
627 }
628 }
629 )+
630 }
631 }
632
633 if length.is_none() {
635 length = Some(len);
636 } else if length.unwrap() != len {
637 return None
638 }
639 }
640
641 if important_count > 0 && important_count != count {
643 return None
644 }
645
646 if $($key.is_some() &&)+ true {
647 let values = izip!(
648 $(
649 $key.unwrap().drain(..),
650 )+
651 ).map(|($($key,)+)| {
652 $name {
653 $(
654 $key,
655 )+
656 }
657 }).collect();
658 return Some((values, important_count > 0))
659 }
660
661 None
662 }
663
664 #[allow(unused_variables)]
665 fn longhands(vendor_prefix: crate::vendor_prefix::VendorPrefix) -> Vec<PropertyId<'static>> {
666 vec![$(PropertyId::$prop$((vp_name!($vp, vendor_prefix)))?, )+]
667 }
668
669 fn longhand(&self, property_id: &PropertyId) -> Option<Property<'i>> {
670 match property_id {
671 $(
672 PropertyId::$prop$((vp_name!($vp, p)))? => {
673 Some(Property::$prop(self.iter().map(|v| v.$key.clone()).collect() $(, *vp_name!($vp, p))?))
674 }
675 )+
676 _ => None
677 }
678 }
679
680 fn set_longhand(&mut self, property: &Property<'i>) -> Result<(), ()> {
681 match property {
682 $(
683 Property::$prop(val $(, vp_name!($vp, _p))?) => {
684 if val.len() != self.len() {
685 return Err(())
686 }
687
688 for (i, item) in self.iter_mut().enumerate() {
689 item.$key = val[i].clone();
690 }
691 return Ok(())
692 }
693 )+
694 _ => {}
695 }
696 Err(())
697 }
698 }
699 };
700}
701
702pub(crate) use define_list_shorthand;
703
704macro_rules! rect_shorthand {
705 (
706 $(#[$meta: meta])*
707 $vis:vis struct $name: ident<$t: ty> {
708 $top: ident,
709 $right: ident,
710 $bottom: ident,
711 $left: ident
712 }
713 ) => {
714 define_shorthand! {
715 $(#[$meta])*
716 pub struct $name {
717 top: $top($t),
719 right: $right($t),
721 bottom: $bottom($t),
723 left: $left($t),
725 }
726 }
727
728 impl<'i> Parse<'i> for $name {
729 fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
730 let rect = Rect::parse(input)?;
731 Ok(Self {
732 top: rect.0,
733 right: rect.1,
734 bottom: rect.2,
735 left: rect.3,
736 })
737 }
738 }
739
740 impl ToCss for $name {
741 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
742 where
743 W: std::fmt::Write,
744 {
745 Rect::new(&self.top, &self.right, &self.bottom, &self.left).to_css(dest)
746 }
747 }
748 };
749}
750
751pub(crate) use rect_shorthand;
752
753macro_rules! size_shorthand {
754 (
755 $(#[$outer:meta])*
756 $vis:vis struct $name: ident<$t: ty> {
757 $(#[$a_meta: meta])*
758 $a_key: ident: $a_prop: ident,
759 $(#[$b_meta: meta])*
760 $b_key: ident: $b_prop: ident,
761 }
762 ) => {
763 define_shorthand! {
764 $(#[$outer])*
765 $vis struct $name {
766 $(#[$a_meta])*
767 $a_key: $a_prop($t),
768 $(#[$b_meta])*
769 $b_key: $b_prop($t),
770 }
771 }
772
773 impl<'i> Parse<'i> for $name {
774 fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
775 let size = Size2D::parse(input)?;
776 Ok(Self {
777 $a_key: size.0,
778 $b_key: size.1,
779 })
780 }
781 }
782
783 impl ToCss for $name {
784 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
785 where
786 W: std::fmt::Write,
787 {
788 Size2D(&self.$a_key, &self.$b_key).to_css(dest)
789 }
790 }
791 };
792}
793
794pub(crate) use size_shorthand;