parcel_css/
macros.rs

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        // !important flags must match to produce a shorthand.
452        if important_count > 0 && important_count != count {
453          return None
454        }
455
456        if $($($prop.is_some() &&)+)+ true {
457          // All properties in the group must have a matching value to produce a shorthand.
458          $(
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 more than one longhand maps to this key, bail.
527                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          // Lengths must be equal.
634          if length.is_none() {
635            length = Some(len);
636          } else if length.unwrap() != len {
637            return None
638          }
639        }
640
641        // !important flags must match to produce a shorthand.
642        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        /// The top value.
718        top: $top($t),
719        /// The right value.
720        right: $right($t),
721        /// The bottom value.
722        bottom: $bottom($t),
723        /// The left value.
724        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;