lightningcss/
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    #[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      /// Returns a string representation of the value.
26      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      /// Returns a string representation of the value.
75      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        // !important flags must match to produce a shorthand.
464        if important_count > 0 && important_count != count {
465          return None
466        }
467
468        if $($(paste! { [<$prop:snake _value>].is_some() } &&)+)+ true {
469          // All properties in the group must have a matching value to produce a shorthand.
470          $(
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 more than one longhand maps to this key, bail.
541                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          // Lengths must be equal.
651          if length.is_none() {
652            length = Some(len);
653          } else if length.unwrap() != len {
654            return None
655          }
656        }
657
658        // !important flags must match to produce a shorthand.
659        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        /// The top value.
735        top: $top($t),
736        /// The right value.
737        right: $right($t),
738        /// The bottom value.
739        bottom: $bottom($t),
740        /// The left value.
741        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;