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