parcel_css 1.0.0-alpha.22

A CSS parser, transformer, and minifier
Documentation
macro_rules! enum_property {
  (
    $(#[$outer:meta])*
    $vis:vis enum $name:ident {
      $( $x: ident, )+
    }
  ) => {
    $(#[$outer])*
    #[derive(Debug, Clone, Copy, PartialEq)]
    $vis enum $name {
      $(
        $x,
      )+
    }

    impl<'i> Parse<'i> for $name {
      fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
        let ident = input.expect_ident()?;
        match &ident[..] {
          $(
            s if s.eq_ignore_ascii_case(stringify!($x)) => Ok($name::$x),
          )+
          _ => return Err(input.new_error(BasicParseErrorKind::QualifiedRuleInvalid))
        }
      }
    }

    impl ToCss for $name {
      fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError> where W: std::fmt::Write {
        use $name::*;
        match self {
          $(
            $x => dest.write_str(&stringify!($x).to_lowercase()),
          )+
        }
      }
    }

    impl $name {
      #[allow(dead_code)]
      pub fn from_str(s: &str) -> Option<Self> {
        match s {
          $(
            s if s.eq_ignore_ascii_case(stringify!($x)) => Some($name::$x),
          )+
          _ => None
        }
      }
    }
  };
  (
    $(#[$outer:meta])*
    $vis:vis enum $name:ident {
      $( $str: literal: $id: ident, )+
    }
  ) => {
    $(#[$outer])*
    #[derive(Debug, Clone, Copy, PartialEq)]
    $vis enum $name {
      $(
        $id,
      )+
    }

    impl<'i> Parse<'i> for $name {
      fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
        let ident = input.expect_ident()?;
        match &ident[..] {
          $(
            s if s.eq_ignore_ascii_case($str) => Ok($name::$id),
          )+
          _ => return Err(input.new_error(BasicParseErrorKind::QualifiedRuleInvalid))
        }
      }
    }

    impl ToCss for $name {
      fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError> where W: std::fmt::Write {
        use $name::*;
        match self {
          $(
            $id => dest.write_str($str),
          )+
        }
      }
    }

    impl $name {
      #[allow(dead_code)]
      pub fn from_str(s: &str) -> Option<Self> {
        match s {
          $(
            s if s.eq_ignore_ascii_case($str) => Some($name::$id),
          )+
          _ => None
        }
      }
    }
  };
}

pub(crate) use enum_property;

macro_rules! shorthand_property {
  (
    $name: ident$(<$l: lifetime>)?
    { $first_key: ident: $first_type: ty, $( $key: ident: $type: ty, )* }
  ) => {
    #[derive(Debug, Clone, PartialEq)]
    pub struct $name$(<$l>)? {
      pub $first_key: $first_type,
      $(
        pub $key: $type,
      )*
    }

    impl<'i> Parse<'i> for $name$(<$l>)? {
      fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
        let mut $first_key = None;
        $(
          let mut $key = None;
        )*

        macro_rules! parse_one {
          ($k: ident, $t: ty) => {
            if $k.is_none() {
              if let Ok(val) = input.try_parse(<$t>::parse) {
                $k = Some(val);
                continue
              }
            }
          };
        }

        loop {
          parse_one!($first_key, $first_type);
          $(
            parse_one!($key, $type);
          )*
          break
        }

        Ok($name {
          $first_key: $first_key.unwrap_or_default(),
          $(
            $key: $key.unwrap_or_default(),
          )*
        })
      }
    }

    impl$(<$l>)? ToCss for $name$(<$l>)? {
      fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError> where W: std::fmt::Write {
        let mut needs_space = false;
        macro_rules! print_one {
          ($k: ident, $t: ty) => {
            if self.$k != <$t>::default() {
              if needs_space {
                dest.write_char(' ')?;
              }
              self.$k.to_css(dest)?;
              needs_space = true;
            }
          };
        }

        print_one!($first_key, $first_type);
        $(
          print_one!($key, $type);
        )*
        if !needs_space {
          self.$first_key.to_css(dest)?;
        }
        Ok(())
      }
    }
  };
}

pub(crate) use shorthand_property;

macro_rules! shorthand_handler {
  (
    $name: ident -> $shorthand: ident$(<$l: lifetime>)?
    { $( $key: ident: $prop: ident($type: ty $(, fallback: $fallback: literal)?), )+ }
  ) => {
    #[derive(Default)]
    pub(crate) struct $name$(<$l>)? {
      targets: Option<Browsers>,
      $(
        pub $key: Option<$type>,
      )*
      has_any: bool
    }

    impl$(<$l>)? $name$(<$l>)? {
      pub fn new(targets: Option<Browsers>) -> Self {
        Self {
          targets,
          ..Self::default()
        }
      }
    }

    impl<'i> PropertyHandler<'i> for $name$(<$l>)? {
      fn handle_property(&mut self, property: &Property<'i>, dest: &mut DeclarationList<'i>, context: &mut PropertyHandlerContext<'i>) -> bool {
        match property {
          $(
            Property::$prop(val) => {
              self.$key = Some(val.clone());
              self.has_any = true;
            },
          )+
          Property::$shorthand(val) => {
            $(
              self.$key = Some(val.$key.clone());
            )+
            self.has_any = true;
          }
          Property::Unparsed(val) if matches!(val.property_id, $( PropertyId::$prop | )+ PropertyId::$shorthand) => {
            self.finalize(dest, context);

            let mut unparsed = val.clone();
            context.add_unparsed_fallbacks(&mut unparsed);
            dest.push(Property::Unparsed(unparsed));
          }
          _ => return false
        }

        true
      }

      fn finalize(&mut self, dest: &mut DeclarationList<'i>, _: &mut PropertyHandlerContext<'i>) {
        if !self.has_any {
          return
        }

        self.has_any = false;

        $(
          let $key = std::mem::take(&mut self.$key);
        )+

        if $( $key.is_some() && )* true {
          let mut shorthand = $shorthand {
            $(
              $key: $key.unwrap(),
            )+
          };

          if let Some(targets) = self.targets {
            let fallbacks = shorthand.get_fallbacks(targets);
            for fallback in fallbacks {
              dest.push(Property::$shorthand(fallback));
            }
          }

          dest.push(Property::$shorthand(shorthand))
        } else {
          $(
            #[allow(unused_mut)]
            if let Some(mut val) = $key {
              $(
                if $fallback {
                  if let Some(targets) = self.targets {
                    let fallbacks = val.get_fallbacks(targets);
                    for fallback in fallbacks {
                      dest.push(Property::$prop(fallback));
                    }
                  }
                }
              )?

              dest.push(Property::$prop(val))
            }
          )+
        }
      }
    }
  };
}

pub(crate) use shorthand_handler;