From e1371c6a77fe156d954b3fc35829b8bba44e6df9 Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Sun, 20 Feb 2022 13:41:12 -0500 Subject: [PATCH 01/16] Experiment: custom at rules --- examples/custom_at_rule.rs | 178 ++++++++++++++++++++++++++++++++++ src/bundler.rs | 28 +++--- src/declaration.rs | 14 +-- src/logical.rs | 2 +- src/parser.rs | 192 +++++++++++++++++++++++++++---------- src/properties/mod.rs | 8 +- src/rules/document.rs | 8 +- src/rules/layer.rs | 6 +- src/rules/media.rs | 10 +- src/rules/mod.rs | 48 +++++----- src/rules/nesting.rs | 10 +- src/rules/style.rs | 14 +-- src/rules/supports.rs | 10 +- src/selector.rs | 24 ++--- src/stylesheet.rs | 16 ++-- 15 files changed, 425 insertions(+), 143 deletions(-) create mode 100644 examples/custom_at_rule.rs diff --git a/examples/custom_at_rule.rs b/examples/custom_at_rule.rs new file mode 100644 index 00000000..b228c4b2 --- /dev/null +++ b/examples/custom_at_rule.rs @@ -0,0 +1,178 @@ +use cssparser::*; +use parcel_css::{ + stylesheet::{StyleSheet, ParserOptions, PrinterOptions}, + values::color::CssColor, + properties::Property +}; + +fn main() { + let args: Vec = std::env::args().collect(); + let source = std::fs::read_to_string(&args[1]).unwrap(); + let opts = ParserOptions { + at_rule_parser: Some(TailwindAtRuleParser), + nesting: true, + custom_media: false, + css_modules: false, + source_index: 0, + }; + + let stylesheet = StyleSheet::parse( + args[1].clone(), + &source, + opts + ).unwrap(); + + println!("{:?}", stylesheet); + + let result = stylesheet.to_css(PrinterOptions::default()).unwrap(); + println!("{}", result.code); +} + +/// An @tailwind directive. +#[derive(Debug)] +enum TailwindDirective { + Base, + Components, + Utilities, + Variants +} + +/// A custom at rule prelude. +enum Prelude { + Tailwind(TailwindDirective), + Apply(Vec, bool) +} + +/// A @tailwind rule. +#[derive(Debug)] +struct TailwindRule { + directive: TailwindDirective, + loc: SourceLocation +} + +/// An @apply rule. +#[derive(Debug)] +struct ApplyRule { + names: Vec, + important: bool, + loc: SourceLocation +} + +/// A custom at rule. +#[derive(Debug)] +enum AtRule { + Tailwind(TailwindRule), + Apply(ApplyRule) +} + +#[derive(Debug)] +struct TailwindAtRuleParser; +impl<'i> AtRuleParser<'i> for TailwindAtRuleParser { + type Prelude = Prelude; + type Error = (); + type AtRule = AtRule; + + fn parse_prelude<'t>( + &mut self, + name: CowRcStr<'i>, + input: &mut Parser<'i, 't>, + ) -> Result> { + match_ignore_ascii_case! {&*name, + "tailwind" => { + let location = input.current_source_location(); + let ident = input.expect_ident()?; + let directive = match_ignore_ascii_case! { &*ident, + "base" => TailwindDirective::Base, + "components" => TailwindDirective::Components, + "utilities" => TailwindDirective::Utilities, + "variants" => TailwindDirective::Variants, + _ => return Err(location.new_unexpected_token_error( + Token::Ident(ident.clone()) + )) + }; + Ok(Prelude::Tailwind(directive)) + }, + "apply" => { + let mut names = Vec::new(); + loop { + if let Ok(name) = input.try_parse(|input| input.expect_ident_cloned()) { + names.push(name.as_ref().into()); + } else { + break + } + } + + let important = input.try_parse(|input| { + input.expect_delim('!')?; + input.expect_ident_matching("important") + }).is_ok(); + + Ok(Prelude::Apply(names, important)) + }, + _ => Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name))) + } + } + + fn rule_without_block( + &mut self, + prelude: Self::Prelude, + start: &ParserState, + ) -> Result { + let loc = start.source_location(); + match prelude { + Prelude::Tailwind(directive) => { + Ok(AtRule::Tailwind(TailwindRule { + directive, + loc + })) + } + Prelude::Apply(names, important) => { + Ok(AtRule::Apply(ApplyRule { + names, + important, + loc + })) + } + } + } +} + +// TODO: expose printer?? +impl cssparser::ToCss for AtRule { + fn to_css(&self, dest: &mut W) -> std::fmt::Result where W: std::fmt::Write { + match self { + AtRule::Tailwind(rule) => { + let _ = rule.loc; // TODO: source maps + let directive = match rule.directive { + TailwindDirective::Base => "TAILWIND BASE HERE", + TailwindDirective::Components => "TAILWIND COMPONENTS HERE", + TailwindDirective::Utilities => "TAILWIND UTILITIES HERE", + TailwindDirective::Variants => "TAILWIND VARIANTS HERE" + }; + dest.write_str(directive) + } + AtRule::Apply(rule) => { + // TODO: possibly better to expose a way to transform these rules to inline the + // declarations rather than doing it during printing. + let _ = rule.loc; // TODO: source maps + for name in &rule.names { + match name.as_ref() { + "bg-blue-400" => { + let color = Color::RGBA(RGBA { red: 0, green: 0, blue: 255, alpha: 255 }); + let property = Property::BackgroundColor(CssColor(color)); + property.temp_to_css(dest, rule.important)?; + } + "text-red-400" => { + let color = Color::RGBA(RGBA { red: 255, green: 0, blue: 0, alpha: 255 }); + let property = Property::Color(CssColor(color)); + property.temp_to_css(dest, rule.important)?; + } + _ => {} + } + dest.write_str("; ")?; + } + Ok(()) + } + } + } +} diff --git a/src/bundler.rs b/src/bundler.rs index 34680aa2..7f03b2b5 100644 --- a/src/bundler.rs +++ b/src/bundler.rs @@ -1,3 +1,4 @@ +use cssparser::AtRuleParser; use parcel_sourcemap::SourceMap; use crate::{rules::{Location, layer::{LayerBlockRule, LayerName}}, error::ErrorLocation}; use std::{fs, path::{Path, PathBuf}, sync::Mutex, collections::HashSet}; @@ -14,17 +15,17 @@ use crate::{ error::{Error, ParserError} }; -pub struct Bundler<'a, 's, P> { +pub struct Bundler<'a, 's, P, T, R> { source_map: Option>, fs: &'a P, source_indexes: DashMap, - stylesheets: Mutex>>, - options: ParserOptions + stylesheets: Mutex>>, + options: ParserOptions } #[derive(Debug)] -struct BundleStyleSheet<'i> { - stylesheet: Option>, +struct BundleStyleSheet<'i, T, R> { + stylesheet: Option>, dependencies: Vec, parent_source_index: u32, parent_dep_index: u32, @@ -103,8 +104,11 @@ impl<'i> BundleErrorKind<'i> { } } -impl<'a, 's, P: SourceProvider> Bundler<'a, 's, P> { - pub fn new(fs: &'a P, source_map: Option<&'s mut SourceMap>, options: ParserOptions) -> Self { +impl<'a, 's, P: SourceProvider, T: AtRuleParser<'a> + Clone + Sync + Send> Bundler<'a, 's, P, T, T::AtRule> +where + T::AtRule: Sync + Send + cssparser::ToCss +{ + pub fn new(fs: &'a P, source_map: Option<&'s mut SourceMap>, options: ParserOptions) -> Self { Bundler { source_map: source_map.map(Mutex::new), fs, @@ -114,7 +118,7 @@ impl<'a, 's, P: SourceProvider> Bundler<'a, 's, P> { } } - pub fn bundle<'e>(&mut self, entry: &'e Path) -> Result, Error>> { + pub fn bundle<'e>(&mut self, entry: &'e Path) -> Result, Error>> { // Phase 1: load and parse all files. This is done in parallel. self.load_file(&entry, ImportRule { url: "".into(), @@ -132,7 +136,7 @@ impl<'a, 's, P: SourceProvider> Bundler<'a, 's, P> { self.order(); // Phase 3: concatenate. - let mut rules: Vec> = Vec::new(); + let mut rules: Vec> = Vec::new(); self.inline(&mut rules); let sources = self.stylesheets.get_mut() @@ -337,7 +341,7 @@ impl<'a, 's, P: SourceProvider> Bundler<'a, 's, P> { &mut HashSet::new() ); - fn process(stylesheets: &mut Vec>, source_index: u32, visited: &mut HashSet) { + fn process<'a, T: AtRuleParser<'a>>(stylesheets: &mut Vec>, source_index: u32, visited: &mut HashSet) { if visited.contains(&source_index) { return } @@ -357,14 +361,14 @@ impl<'a, 's, P: SourceProvider> Bundler<'a, 's, P> { } } - fn inline(&mut self, dest: &mut Vec>) { + fn inline(&mut self, dest: &mut Vec>) { process( self.stylesheets.get_mut().unwrap(), 0, dest ); - fn process<'a>(stylesheets: &mut Vec>, source_index: u32, dest: &mut Vec>) { + fn process<'a, T: AtRuleParser<'a>>(stylesheets: &mut Vec>, source_index: u32, dest: &mut Vec>) { let stylesheet = &mut stylesheets[source_index as usize]; let mut rules = std::mem::take(&mut stylesheet.stylesheet.as_mut().unwrap().rules.0); diff --git a/src/declaration.rs b/src/declaration.rs index 42dd5de6..ca3fc24e 100644 --- a/src/declaration.rs +++ b/src/declaration.rs @@ -34,7 +34,7 @@ pub struct DeclarationBlock<'i> { } impl<'i> DeclarationBlock<'i> { - pub fn parse<'t>(input: &mut Parser<'i, 't>, options: &ParserOptions) -> Result>> { + pub fn parse<'t, T>(input: &mut Parser<'i, 't>, options: &ParserOptions) -> Result>> { let mut important_declarations = DeclarationList::new(); let mut declarations = DeclarationList::new(); let mut parser = DeclarationListParser::new(input, PropertyDeclarationParser { @@ -115,14 +115,14 @@ impl<'i> DeclarationBlock<'i> { } } -struct PropertyDeclarationParser<'a, 'i> { +struct PropertyDeclarationParser<'a, 'i, T> { important_declarations: &'a mut Vec>, declarations: &'a mut Vec>, - options: &'a ParserOptions + options: &'a ParserOptions } /// Parse a declaration within {} block: `color: blue` -impl<'a, 'i> cssparser::DeclarationParser<'i> for PropertyDeclarationParser<'a, 'i> { +impl<'a, 'i, T> cssparser::DeclarationParser<'i> for PropertyDeclarationParser<'a, 'i, T> { type Declaration = (); type Error = ParserError<'i>; @@ -136,18 +136,18 @@ impl<'a, 'i> cssparser::DeclarationParser<'i> for PropertyDeclarationParser<'a, } /// Default methods reject all at rules. -impl<'a, 'i> AtRuleParser<'i> for PropertyDeclarationParser<'a, 'i> { +impl<'a, 'i, T> AtRuleParser<'i> for PropertyDeclarationParser<'a, 'i, T> { type Prelude = (); type AtRule = (); type Error = ParserError<'i>; } -pub(crate) fn parse_declaration<'i, 't>( +pub(crate) fn parse_declaration<'i, 't, T>( name: CowRcStr<'i>, input: &mut cssparser::Parser<'i, 't>, declarations: &mut DeclarationList<'i>, important_declarations: &mut DeclarationList<'i>, - options: &ParserOptions + options: &ParserOptions ) -> Result<(), cssparser::ParseError<'i, ParserError<'i>>> { let property = input.parse_until_before(Delimiter::Bang, |input| Property::parse(name, input, options))?; let important = input.try_parse(|input| { diff --git a/src/logical.rs b/src/logical.rs index 7c11b8ff..a60e5b82 100644 --- a/src/logical.rs +++ b/src/logical.rs @@ -65,7 +65,7 @@ impl LogicalProperties { })); } - pub fn to_rules(&mut self, dest: &mut CssRuleList) { + pub fn to_rules(&mut self, dest: &mut CssRuleList) { // Generate rules for [dir="ltr"] and [dir="rtl"] to define --ltr and --rtl vars. macro_rules! style_rule { ($dir: ident, $ltr: expr, $rtl: expr) => { diff --git a/src/parser.rs b/src/parser.rs index ce2d7476..3b126d7b 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -30,12 +30,40 @@ use crate::vendor_prefix::VendorPrefix; use std::collections::HashMap; use crate::error::ParserError; -#[derive(Default, Clone, Debug)] -pub struct ParserOptions { +#[derive(Clone, Debug)] +pub struct ParserOptions { pub nesting: bool, pub custom_media: bool, pub css_modules: bool, - pub source_index: u32 + pub source_index: u32, + pub at_rule_parser: Option +} + +impl ParserOptions { + pub fn default() -> Self { + ParserOptions { + nesting: false, + custom_media: false, + css_modules: false, + source_index: 0, + at_rule_parser: None + } + } +} + +#[derive(Clone)] +pub struct DefaultAtRuleParser; +impl<'i> AtRuleParser<'i> for DefaultAtRuleParser { + type AtRule = DefaultAtRule; + type Error = (); + type Prelude = (); +} + +pub struct DefaultAtRule; +impl cssparser::ToCss for DefaultAtRule { + fn to_css(&self, _: &mut W) -> std::fmt::Result where W: std::fmt::Write { + Err(std::fmt::Error) + } } #[derive(PartialEq, PartialOrd)] @@ -48,15 +76,15 @@ enum State { } /// The parser for the top-level rules in a stylesheet. -pub struct TopLevelRuleParser<'a, 'i> { +pub struct TopLevelRuleParser<'a, 'i, T> { default_namespace: Option>, namespace_prefixes: HashMap, CowArcStr<'i>>, - options: &'a ParserOptions, + options: &'a mut ParserOptions, state: State } -impl<'a, 'b, 'i> TopLevelRuleParser<'a, 'i> { - pub fn new(options: &'a ParserOptions) -> TopLevelRuleParser<'a, 'i> { +impl<'a, 'b, 'i, T: AtRuleParser<'i>> TopLevelRuleParser<'a, 'i, T> { + pub fn new(options: &'a mut ParserOptions) -> TopLevelRuleParser<'a, 'i, T> { TopLevelRuleParser { default_namespace: None, namespace_prefixes: HashMap::new(), @@ -65,11 +93,11 @@ impl<'a, 'b, 'i> TopLevelRuleParser<'a, 'i> { } } - fn nested<'x: 'b>(&'x mut self) -> NestedRuleParser<'_, 'i> { + fn nested<'x: 'b>(&'x mut self) -> NestedRuleParser<'_, 'i, T> { NestedRuleParser { default_namespace: &mut self.default_namespace, namespace_prefixes: &mut self.namespace_prefixes, - options: &self.options + options: &mut self.options } } } @@ -77,7 +105,7 @@ impl<'a, 'b, 'i> TopLevelRuleParser<'a, 'i> { /// A rule prelude for at-rule with block. #[derive(Debug)] #[allow(dead_code)] -pub enum AtRulePrelude<'i> { +pub enum AtRulePrelude<'i, T> { /// A @font-face rule prelude. FontFace, /// A @font-feature-values rule prelude, with its FamilyName list. @@ -106,12 +134,13 @@ pub enum AtRulePrelude<'i> { Charset, /// A @nest prelude. Nest(SelectorList<'i, Selectors>), - Layer(Vec>) + Layer(Vec>), + Custom(T) } -impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a, 'i> { - type Prelude = AtRulePrelude<'i>; - type AtRule = (SourcePosition, CssRule<'i>); +impl<'a, 'i, T: AtRuleParser<'i>> AtRuleParser<'i> for TopLevelRuleParser<'a, 'i, T> { + type Prelude = AtRulePrelude<'i, T::Prelude>; + type AtRule = (SourcePosition, CssRule<'i, T::AtRule>); type Error = ParserError<'i>; fn parse_prelude<'t>( @@ -192,7 +221,7 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a, 'i> { #[inline] fn rule_without_block( &mut self, - prelude: AtRulePrelude<'i>, + prelude: AtRulePrelude<'i, T::Prelude>, start: &ParserState, ) -> Result { let loc = start.source_location(); @@ -246,6 +275,10 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a, 'i> { AtRuleParser::rule_without_block(&mut self.nested(), prelude, start)? }, AtRulePrelude::Charset => CssRule::Ignored, + AtRulePrelude::Custom(_) => { + self.state = State::Body; + AtRuleParser::rule_without_block(&mut self.nested(), prelude, start)? + }, _ => return Err(()) }; @@ -253,9 +286,9 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a, 'i> { } } -impl<'a, 'i> QualifiedRuleParser<'i> for TopLevelRuleParser<'a, 'i> { +impl<'a, 'i, T: AtRuleParser<'i>> QualifiedRuleParser<'i> for TopLevelRuleParser<'a, 'i, T> { type Prelude = SelectorList<'i, Selectors>; - type QualifiedRule = (SourcePosition, CssRule<'i>); + type QualifiedRule = (SourcePosition, CssRule<'i, T::AtRule>); type Error = ParserError<'i>; #[inline] @@ -279,15 +312,14 @@ impl<'a, 'i> QualifiedRuleParser<'i> for TopLevelRuleParser<'a, 'i> { } } -#[derive(Clone)] -struct NestedRuleParser<'a, 'i> { +struct NestedRuleParser<'a, 'i, T> { default_namespace: &'a Option>, namespace_prefixes: &'a HashMap, CowArcStr<'i>>, - options: &'a ParserOptions + options: &'a mut ParserOptions } -impl<'a, 'b, 'i> NestedRuleParser<'a, 'i> { - fn parse_nested_rules<'t>(&mut self, input: &mut Parser<'i, 't>) -> CssRuleList<'i> { +impl<'a, 'b, 'i, T: AtRuleParser<'i>> NestedRuleParser<'a, 'i, T> { + fn parse_nested_rules<'t>(&mut self, input: &mut Parser<'i, 't>) -> CssRuleList<'i, T::AtRule> { let nested_parser = NestedRuleParser { default_namespace: self.default_namespace, namespace_prefixes: self.namespace_prefixes, @@ -319,9 +351,9 @@ impl<'a, 'b, 'i> NestedRuleParser<'a, 'i> { } } -impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'i> { - type Prelude = AtRulePrelude<'i>; - type AtRule = CssRule<'i>; +impl<'a, 'b, 'i, T: AtRuleParser<'i>> AtRuleParser<'i> for NestedRuleParser<'a, 'i, T> { + type Prelude = AtRulePrelude<'i, T::Prelude>; + type AtRule = CssRule<'i, T::AtRule>; type Error = ParserError<'i>; fn parse_prelude<'t>( @@ -403,7 +435,15 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'i> { }; Ok(AtRulePrelude::Layer(names)) }, - _ => Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name))) + _ => { + if let Some(at_rule_parser) = &mut self.options.at_rule_parser { + at_rule_parser.parse_prelude(name.clone(), input) + .map(|prelude| AtRulePrelude::Custom(prelude)) + .map_err(|_| input.new_error(BasicParseErrorKind::AtRuleInvalid(name))) + } else { + Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name))) + } + } } } @@ -412,7 +452,7 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'i> { prelude: Self::Prelude, start: &ParserState, input: &mut Parser<'i, 't>, - ) -> Result, ParseError<'i, Self::Error>> { + ) -> Result, ParseError<'i, Self::Error>> { let loc = self.loc(start); match prelude { AtRulePrelude::FontFace => { @@ -518,14 +558,23 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'i> { // These rules don't have blocks. Err(input.new_unexpected_token_error(Token::CurlyBracketBlock)) }, - AtRulePrelude::FontFeatureValues | AtRulePrelude::Nest(..) => unreachable!() + AtRulePrelude::FontFeatureValues | AtRulePrelude::Nest(..) => unreachable!(), + AtRulePrelude::Custom(prelude) => { + if let Some(at_rule_parser) = &mut self.options.at_rule_parser { + at_rule_parser.parse_block(prelude, start, input) + .map(|prelude| CssRule::Custom(prelude)) + .map_err(|_| input.new_error(BasicParseErrorKind::AtRuleBodyInvalid)) + } else { + Err(input.new_error(BasicParseErrorKind::AtRuleBodyInvalid)) + } + } } } #[inline] fn rule_without_block( &mut self, - prelude: AtRulePrelude<'i>, + prelude: AtRulePrelude<'i, T::Prelude>, start: &ParserState, ) -> Result { let loc = self.loc(start); @@ -540,14 +589,22 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'i> { loc })) } + AtRulePrelude::Custom(prelude) => { + if let Some(at_rule_parser) = &mut self.options.at_rule_parser { + at_rule_parser.rule_without_block(prelude, start) + .map(|prelude| CssRule::Custom(prelude)) + } else { + Err(()) + } + } _ => Err(()) } } } -impl<'a, 'b, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'i> { +impl<'a, 'b, 'i, T: AtRuleParser<'i>> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'i, T> { type Prelude = SelectorList<'i, Selectors>; - type QualifiedRule = CssRule<'i>; + type QualifiedRule = CssRule<'i, T::AtRule>; type Error = ParserError<'i>; fn parse_prelude<'t>( @@ -568,7 +625,7 @@ impl<'a, 'b, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'i> { selectors: Self::Prelude, start: &ParserState, input: &mut Parser<'i, 't>, - ) -> Result, ParseError<'i, Self::Error>> { + ) -> Result, ParseError<'i, Self::Error>> { let loc = self.loc(start); let (declarations, rules) = if self.options.nesting { parse_declarations_and_nested_rules(input, self.default_namespace, self.namespace_prefixes, self.options)? @@ -585,12 +642,12 @@ impl<'a, 'b, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'i> { } } -fn parse_declarations_and_nested_rules<'a, 'i, 't>( +fn parse_declarations_and_nested_rules<'a, 'i, 't, T: AtRuleParser<'i>>( input: &mut Parser<'i, 't>, default_namespace: &'a Option>, namespace_prefixes: &'a HashMap, CowArcStr<'i>>, - options: &'a ParserOptions -) -> Result<(DeclarationBlock<'i>, CssRuleList<'i>), ParseError<'i, ParserError<'i>>> { + options: &'a mut ParserOptions +) -> Result<(DeclarationBlock<'i>, CssRuleList<'i, T::AtRule>), ParseError<'i, ParserError<'i>>> { let mut important_declarations = DeclarationList::new(); let mut declarations = DeclarationList::new(); let mut rules = CssRuleList(vec![]); @@ -627,17 +684,17 @@ fn parse_declarations_and_nested_rules<'a, 'i, 't>( Ok((DeclarationBlock { declarations, important_declarations }, rules)) } -pub struct StyleRuleParser<'a, 'i> { +pub struct StyleRuleParser<'a, 'i, T, R> { default_namespace: &'a Option>, namespace_prefixes: &'a HashMap, CowArcStr<'i>>, - options: &'a ParserOptions, + options: &'a mut ParserOptions, declarations: &'a mut DeclarationList<'i>, important_declarations: &'a mut DeclarationList<'i>, - rules: &'a mut CssRuleList<'i> + rules: &'a mut CssRuleList<'i, R> } /// Parse a declaration within {} block: `color: blue` -impl<'a, 'i> cssparser::DeclarationParser<'i> for StyleRuleParser<'a, 'i> { +impl<'a, 'i, T: AtRuleParser<'i>> cssparser::DeclarationParser<'i> for StyleRuleParser<'a, 'i, T, T::AtRule> { type Declaration = (); type Error = ParserError<'i>; @@ -654,8 +711,8 @@ impl<'a, 'i> cssparser::DeclarationParser<'i> for StyleRuleParser<'a, 'i> { } } -impl<'a, 'i> AtRuleParser<'i> for StyleRuleParser<'a, 'i> { - type Prelude = AtRulePrelude<'i>; +impl<'a, 'i, T: AtRuleParser<'i>> AtRuleParser<'i> for StyleRuleParser<'a, 'i, T, T::AtRule> { + type Prelude = AtRulePrelude<'i, T::Prelude>; type AtRule = (); type Error = ParserError<'i>; @@ -683,13 +740,21 @@ impl<'a, 'i> AtRuleParser<'i> for StyleRuleParser<'a, 'i> { let selectors = SelectorList::parse(&selector_parser, input, NestingRequirement::Contained)?; Ok(AtRulePrelude::Nest(selectors)) }, - _ => Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name))) + _ => { + if let Some(at_rule_parser) = &mut self.options.at_rule_parser { + at_rule_parser.parse_prelude(name.clone(), input) + .map(|prelude| AtRulePrelude::Custom(prelude)) + .map_err(|_| input.new_error(BasicParseErrorKind::AtRuleInvalid(name))) + } else { + Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name))) + } + } } } fn parse_block<'t>( &mut self, - prelude: AtRulePrelude<'i>, + prelude: AtRulePrelude<'i, T::Prelude>, start: &ParserState, input: &mut Parser<'i, 't>, ) -> Result<(), ParseError<'i, Self::Error>> { @@ -730,22 +795,49 @@ impl<'a, 'i> AtRuleParser<'i> for StyleRuleParser<'a, 'i> { })); Ok(()) }, - _ => { - println!("{:?}", prelude); - unreachable!() + AtRulePrelude::Custom(prelude) => { + if let Some(at_rule_parser) = &mut self.options.at_rule_parser { + let rule = at_rule_parser.parse_block(prelude, start, input) + .map_err(|_| input.new_error(BasicParseErrorKind::AtRuleBodyInvalid))?; + self.rules.0.push(CssRule::Custom(rule)); + Ok(()) + } else { + Err(input.new_error(BasicParseErrorKind::AtRuleBodyInvalid)) + } + }, + _ => Err(input.new_error(BasicParseErrorKind::AtRuleBodyInvalid)) + } + } + + #[inline] + fn rule_without_block( + &mut self, + prelude: AtRulePrelude<'i, T::Prelude>, + start: &ParserState, + ) -> Result { + match prelude { + AtRulePrelude::Custom(prelude) => { + if let Some(at_rule_parser) = &mut self.options.at_rule_parser { + let rule = at_rule_parser.rule_without_block(prelude, start)?; + self.rules.0.push(CssRule::Custom(rule)); + Ok(()) + } else { + Err(()) + } } + _ => Err(()) } } } #[inline] -fn parse_nested_at_rule<'a, 'i, 't>( +fn parse_nested_at_rule<'a, 'i, 't, T: AtRuleParser<'i>>( input: &mut Parser<'i, 't>, source_index: u32, default_namespace: &'a Option>, namespace_prefixes: &'a HashMap, CowArcStr<'i>>, - options: &'a ParserOptions -) -> Result, ParseError<'i, ParserError<'i>>> { + options: &'a mut ParserOptions +) -> Result, ParseError<'i, ParserError<'i>>> { let loc = input.current_source_location(); let loc = Location { source_index, line: loc.line, column: loc.column }; @@ -766,7 +858,7 @@ fn parse_nested_at_rule<'a, 'i, 't>( Ok(rules) } -impl<'a, 'b, 'i> QualifiedRuleParser<'i> for StyleRuleParser<'a, 'i> { +impl<'a, 'b, 'i, T: AtRuleParser<'i>> QualifiedRuleParser<'i> for StyleRuleParser<'a, 'i, T, T::AtRule> { type Prelude = SelectorList<'i, Selectors>; type QualifiedRule = (); type Error = ParserError<'i>; diff --git a/src/properties/mod.rs b/src/properties/mod.rs index a35fa30b..8f9f80f0 100644 --- a/src/properties/mod.rs +++ b/src/properties/mod.rs @@ -293,7 +293,7 @@ macro_rules! define_properties { } impl<'i> Property<'i> { - pub fn parse<'t>(name: CowRcStr<'i>, input: &mut Parser<'i, 't>, options: &ParserOptions) -> Result, ParseError<'i, ParserError<'i>>> { + pub fn parse<'t, T>(name: CowRcStr<'i>, input: &mut Parser<'i, 't>, options: &ParserOptions) -> Result, ParseError<'i, ParserError<'i>>> { let state = input.state(); let name_ref = name.as_ref(); let (prefix, name_ref) = if starts_with_ignore_ascii_case(name_ref, "-webkit-") { @@ -461,6 +461,12 @@ macro_rules! define_properties { write!(VendorPrefix::None); Ok(()) } + + // TODO: temp + pub fn temp_to_css(&self, dest: &mut W, important: bool) -> std::fmt::Result where W: std::fmt::Write { + let mut printer = Printer::new(dest, None, false, None); + self.to_css(&mut printer, important).map_err(|_| std::fmt::Error) + } } }; } diff --git a/src/rules/document.rs b/src/rules/document.rs index 124aca1c..5277f596 100644 --- a/src/rules/document.rs +++ b/src/rules/document.rs @@ -5,18 +5,18 @@ use super::{CssRuleList, MinifyContext}; use crate::error::{MinifyError, PrinterError}; #[derive(Debug, PartialEq, Clone)] -pub struct MozDocumentRule<'i> { - pub rules: CssRuleList<'i>, +pub struct MozDocumentRule<'i, T> { + pub rules: CssRuleList<'i, T>, pub loc: Location } -impl<'i> MozDocumentRule<'i> { +impl<'i, T> MozDocumentRule<'i, T> { pub(crate) fn minify(&mut self, context: &mut MinifyContext<'_, 'i>) -> Result<(), MinifyError> { self.rules.minify(context, false) } } -impl<'i> ToCss for MozDocumentRule<'i> { +impl<'i, T: cssparser::ToCss> ToCss for MozDocumentRule<'i, T> { fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> where W: std::fmt::Write { dest.add_mapping(self.loc); dest.write_str("@-moz-document url-prefix()")?; diff --git a/src/rules/layer.rs b/src/rules/layer.rs index 5c66b162..f8a6255e 100644 --- a/src/rules/layer.rs +++ b/src/rules/layer.rs @@ -84,13 +84,13 @@ impl<'i> ToCss for LayerStatementRule<'i> { /// https://drafts.csswg.org/css-cascade-5/#layer-block #[derive(Debug, Clone, PartialEq)] -pub struct LayerBlockRule<'i> { +pub struct LayerBlockRule<'i, T> { pub name: Option>, - pub rules: CssRuleList<'i>, + pub rules: CssRuleList<'i, T>, pub loc: Location } -impl<'i> ToCss for LayerBlockRule<'i> { +impl<'i, T: cssparser::ToCss> ToCss for LayerBlockRule<'i, T> { fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> where W: std::fmt::Write { dest.add_mapping(self.loc); dest.write_str("@layer")?; diff --git a/src/rules/media.rs b/src/rules/media.rs index f86e9f92..5b3facde 100644 --- a/src/rules/media.rs +++ b/src/rules/media.rs @@ -7,13 +7,13 @@ use crate::rules::{ToCssWithContext, StyleContext}; use crate::error::{MinifyError, PrinterError}; #[derive(Debug, PartialEq, Clone)] -pub struct MediaRule<'i> { +pub struct MediaRule<'i, T> { pub query: MediaList<'i>, - pub rules: CssRuleList<'i>, + pub rules: CssRuleList<'i, T>, pub loc: Location } -impl<'i> MediaRule<'i> { +impl<'i, T> MediaRule<'i, T> { pub(crate) fn minify(&mut self, context: &mut MinifyContext<'_, 'i>, parent_is_unused: bool) -> Result { self.rules.minify(context, parent_is_unused)?; @@ -25,8 +25,8 @@ impl<'i> MediaRule<'i> { } } -impl<'a, 'i> ToCssWithContext<'a, 'i> for MediaRule<'i> { - fn to_css_with_context(&self, dest: &mut Printer, context: Option<&StyleContext<'a, 'i>>) -> Result<(), PrinterError> where W: std::fmt::Write { +impl<'a, 'i, T: cssparser::ToCss> ToCssWithContext<'a, 'i, T> for MediaRule<'i, T> { + fn to_css_with_context(&self, dest: &mut Printer, context: Option<&StyleContext<'a, 'i, T>>) -> Result<(), PrinterError> where W: std::fmt::Write { // If the media query always matches, we can just output the nested rules. if dest.minify && self.query.always_matches() { self.rules.to_css_with_context(dest, context)?; diff --git a/src/rules/mod.rs b/src/rules/mod.rs index 610e0596..c8a21fea 100644 --- a/src/rules/mod.rs +++ b/src/rules/mod.rs @@ -35,18 +35,18 @@ use crate::prefixes::Feature; use crate::targets::Browsers; use std::collections::{HashMap, HashSet}; use crate::selector::{is_equivalent, get_prefix, get_necessary_prefixes}; -use crate::error::{MinifyError, PrinterError}; +use crate::error::{Error, MinifyError, PrinterError, PrinterErrorKind}; use crate::logical::LogicalProperties; use crate::dependencies::{Dependency, ImportDependency}; use self::layer::{LayerBlockRule, LayerStatementRule}; -pub(crate) trait ToCssWithContext<'a, 'i> { - fn to_css_with_context(&self, dest: &mut Printer, context: Option<&StyleContext<'a, 'i>>) -> Result<(), PrinterError> where W: std::fmt::Write; +pub(crate) trait ToCssWithContext<'a, 'i, T> { + fn to_css_with_context(&self, dest: &mut Printer, context: Option<&StyleContext<'a, 'i, T>>) -> Result<(), PrinterError> where W: std::fmt::Write; } -pub(crate) struct StyleContext<'a, 'i> { - pub rule: &'a StyleRule<'i>, - pub parent: Option<&'a StyleContext<'a, 'i>> +pub(crate) struct StyleContext<'a, 'i, T> { + pub rule: &'a StyleRule<'i, T>, + pub parent: Option<&'a StyleContext<'a, 'i, T>> } #[derive(PartialEq, Eq, Debug, Clone, Copy)] @@ -61,27 +61,28 @@ pub struct Location { } #[derive(Debug, PartialEq, Clone)] -pub enum CssRule<'i> { - Media(MediaRule<'i>), +pub enum CssRule<'i, T> { + Media(MediaRule<'i, T>), Import(ImportRule<'i>), - Style(StyleRule<'i>), + Style(StyleRule<'i, T>), Keyframes(KeyframesRule<'i>), FontFace(FontFaceRule<'i>), Page(PageRule<'i>), - Supports(SupportsRule<'i>), + Supports(SupportsRule<'i, T>), CounterStyle(CounterStyleRule<'i>), Namespace(NamespaceRule<'i>), - MozDocument(MozDocumentRule<'i>), - Nesting(NestingRule<'i>), + MozDocument(MozDocumentRule<'i, T>), + Nesting(NestingRule<'i, T>), Viewport(ViewportRule<'i>), CustomMedia(CustomMediaRule<'i>), LayerStatement(LayerStatementRule<'i>), - LayerBlock(LayerBlockRule<'i>), - Ignored + LayerBlock(LayerBlockRule<'i, T>), + Ignored, + Custom(T) } -impl<'a, 'i> ToCssWithContext<'a, 'i> for CssRule<'i> { - fn to_css_with_context(&self, dest: &mut Printer, context: Option<&StyleContext<'a, 'i>>) -> Result<(), PrinterError> where W: std::fmt::Write { +impl<'a, 'i, T: cssparser::ToCss> ToCssWithContext<'a, 'i, T> for CssRule<'i, T> { + fn to_css_with_context(&self, dest: &mut Printer, context: Option<&StyleContext<'a, 'i, T>>) -> Result<(), PrinterError> where W: std::fmt::Write { match self { CssRule::Media(media) => media.to_css_with_context(dest, context), CssRule::Import(import) => import.to_css(dest), @@ -98,19 +99,20 @@ impl<'a, 'i> ToCssWithContext<'a, 'i> for CssRule<'i> { CssRule::CustomMedia(custom_media) => custom_media.to_css(dest), CssRule::LayerStatement(layer) => layer.to_css(dest), CssRule::LayerBlock(layer) => layer.to_css(dest), - CssRule::Ignored => Ok(()) + CssRule::Ignored => Ok(()), + CssRule::Custom(rule) => rule.to_css(dest).map_err(|_| Error { kind: PrinterErrorKind::FmtError, loc: None }), } } } -impl<'i> ToCss for CssRule<'i> { +impl<'i, T: cssparser::ToCss> ToCss for CssRule<'i, T> { fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> where W: std::fmt::Write { self.to_css_with_context(dest, None) } } #[derive(Debug, PartialEq, Clone)] -pub struct CssRuleList<'i>(pub Vec>); +pub struct CssRuleList<'i, T>(pub Vec>); pub(crate) struct MinifyContext<'a, 'i> { pub targets: &'a Option, @@ -121,7 +123,7 @@ pub(crate) struct MinifyContext<'a, 'i> { pub custom_media: Option, CustomMediaRule<'i>>> } -impl<'i> CssRuleList<'i> { +impl<'i, T> CssRuleList<'i, T> { pub(crate) fn minify(&mut self, context: &mut MinifyContext<'_, 'i>, parent_is_unused: bool) -> Result<(), MinifyError> { let mut keyframe_rules = HashMap::new(); let mut rules = Vec::new(); @@ -240,14 +242,14 @@ impl<'i> CssRuleList<'i> { } } -impl<'i> ToCss for CssRuleList<'i> { +impl<'i, T: cssparser::ToCss> ToCss for CssRuleList<'i, T> { fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> where W: std::fmt::Write { self.to_css_with_context(dest, None) } } -impl<'a, 'i> ToCssWithContext<'a, 'i> for CssRuleList<'i> { - fn to_css_with_context(&self, dest: &mut Printer, context: Option<&StyleContext<'a, 'i>>) -> Result<(), PrinterError> where W: std::fmt::Write { +impl<'a, 'i, T: cssparser::ToCss> ToCssWithContext<'a, 'i, T> for CssRuleList<'i, T> { + fn to_css_with_context(&self, dest: &mut Printer, context: Option<&StyleContext<'a, 'i, T>>) -> Result<(), PrinterError> where W: std::fmt::Write { let mut first = true; let mut last_without_block = false; diff --git a/src/rules/nesting.rs b/src/rules/nesting.rs index bb333b2d..db67e1cc 100644 --- a/src/rules/nesting.rs +++ b/src/rules/nesting.rs @@ -6,19 +6,19 @@ use crate::error::{PrinterError, MinifyError}; use super::MinifyContext; #[derive(Debug, PartialEq, Clone)] -pub struct NestingRule<'i> { - pub style: StyleRule<'i>, +pub struct NestingRule<'i, T> { + pub style: StyleRule<'i, T>, pub loc: Location } -impl<'i> NestingRule<'i> { +impl<'i, T> NestingRule<'i, T> { pub(crate) fn minify(&mut self, context: &mut MinifyContext<'_, 'i>, parent_is_unused: bool) -> Result { self.style.minify(context, parent_is_unused) } } -impl<'a, 'i> ToCssWithContext<'a, 'i> for NestingRule<'i> { - fn to_css_with_context(&self, dest: &mut Printer, context: Option<&StyleContext<'a, 'i>>) -> Result<(), PrinterError> where W: std::fmt::Write { +impl<'a, 'i, T: cssparser::ToCss> ToCssWithContext<'a, 'i, T> for NestingRule<'i, T> { + fn to_css_with_context(&self, dest: &mut Printer, context: Option<&StyleContext<'a, 'i, T>>) -> Result<(), PrinterError> where W: std::fmt::Write { dest.add_mapping(self.loc); if context.is_none() { dest.write_str("@nest ")?; diff --git a/src/rules/style.rs b/src/rules/style.rs index e6eb1ce0..33b4e642 100644 --- a/src/rules/style.rs +++ b/src/rules/style.rs @@ -12,15 +12,15 @@ use crate::error::{PrinterError, PrinterErrorKind, MinifyError}; use super::MinifyContext; #[derive(Debug, PartialEq, Clone)] -pub struct StyleRule<'i> { +pub struct StyleRule<'i, T> { pub selectors: SelectorList<'i, Selectors>, pub vendor_prefix: VendorPrefix, pub declarations: DeclarationBlock<'i>, - pub rules: CssRuleList<'i>, + pub rules: CssRuleList<'i, T>, pub loc: Location } -impl<'i> StyleRule<'i> { +impl<'i, T> StyleRule<'i, T> { pub(crate) fn minify(&mut self, context: &mut MinifyContext<'_, 'i>, parent_is_unused: bool) -> Result { let mut unused = false; if !context.unused_symbols.is_empty() { @@ -52,8 +52,8 @@ impl<'i> StyleRule<'i> { } } -impl<'a, 'i> ToCssWithContext<'a, 'i> for StyleRule<'i> { - fn to_css_with_context(&self, dest: &mut Printer, context: Option<&StyleContext<'a, 'i>>) -> Result<(), PrinterError> where W: std::fmt::Write { +impl<'a, 'i, T: cssparser::ToCss> ToCssWithContext<'a, 'i, T> for StyleRule<'i, T> { + fn to_css_with_context(&self, dest: &mut Printer, context: Option<&StyleContext<'a, 'i, T>>) -> Result<(), PrinterError> where W: std::fmt::Write { if self.vendor_prefix.is_empty() { self.to_css_base(dest, context) } else { @@ -88,8 +88,8 @@ impl<'a, 'i> ToCssWithContext<'a, 'i> for StyleRule<'i> { } } -impl<'a, 'i> StyleRule<'i> { - fn to_css_base(&self, dest: &mut Printer, context: Option<&StyleContext<'a, 'i>>) -> Result<(), PrinterError> where W: std::fmt::Write { +impl<'a, 'i, T: cssparser::ToCss> StyleRule<'i, T> { + fn to_css_base(&self, dest: &mut Printer, context: Option<&StyleContext<'a, 'i, T>>) -> Result<(), PrinterError> where W: std::fmt::Write { // If supported, or there are no targets, preserve nesting. Otherwise, write nested rules after parent. let supports_nesting = self.rules.0.is_empty() || dest.targets.is_none() || Feature::CssNesting.is_compatible(dest.targets.unwrap()); let len = self.declarations.declarations.len() + self.declarations.important_declarations.len(); diff --git a/src/rules/supports.rs b/src/rules/supports.rs index cc16daff..43e6fb03 100644 --- a/src/rules/supports.rs +++ b/src/rules/supports.rs @@ -8,20 +8,20 @@ use crate::rules::{ToCssWithContext, StyleContext}; use crate::error::{ParserError, MinifyError, PrinterError}; #[derive(Debug, PartialEq, Clone)] -pub struct SupportsRule<'i> { +pub struct SupportsRule<'i, T> { pub condition: SupportsCondition<'i>, - pub rules: CssRuleList<'i>, + pub rules: CssRuleList<'i, T>, pub loc: Location } -impl<'i> SupportsRule<'i> { +impl<'i, T> SupportsRule<'i, T> { pub(crate) fn minify(&mut self, context: &mut MinifyContext<'_, 'i>, parent_is_unused: bool) -> Result<(), MinifyError> { self.rules.minify(context, parent_is_unused) } } -impl<'a, 'i> ToCssWithContext<'a, 'i> for SupportsRule<'i> { - fn to_css_with_context(&self, dest: &mut Printer, context: Option<&StyleContext<'a, 'i>>) -> Result<(), PrinterError> where W: std::fmt::Write { +impl<'a, 'i, T: cssparser::ToCss> ToCssWithContext<'a, 'i, T> for SupportsRule<'i, T> { + fn to_css_with_context(&self, dest: &mut Printer, context: Option<&StyleContext<'a, 'i, T>>) -> Result<(), PrinterError> where W: std::fmt::Write { dest.add_mapping(self.loc); dest.write_str("@supports ")?; self.condition.to_css(dest)?; diff --git a/src/selector.rs b/src/selector.rs index 09691171..45664b10 100644 --- a/src/selector.rs +++ b/src/selector.rs @@ -1,4 +1,4 @@ -use crate::values::string::CowArcStr; +use crate::{values::string::CowArcStr, parser::DefaultAtRule}; use cssparser::*; use parcel_selectors::{SelectorList, parser::{SelectorImpl, Selector, Combinator, Component}, attr::{AttrSelectorOperator, ParsedAttrSelectorOperation, ParsedCaseSensitivity}}; use std::fmt; @@ -69,7 +69,7 @@ impl<'i> SelectorImpl<'i> for Selectors { fn to_css(selectors: &SelectorList<'i, Self>, dest: &mut W) -> std::fmt::Result { let mut printer = Printer::new(dest, None, false, None); - serialize_selector_list(selectors.0.iter(), &mut printer, None).map_err(|_| std::fmt::Error) + serialize_selector_list::<_, _, DefaultAtRule>(selectors.0.iter(), &mut printer, None).map_err(|_| std::fmt::Error) } } @@ -334,8 +334,8 @@ impl<'i> cssparser::ToCss for PseudoClass<'i> { } } -impl<'a, 'i> ToCssWithContext<'a, 'i> for PseudoClass<'i> { - fn to_css_with_context(&self, dest: &mut Printer, context: Option<&StyleContext<'a, 'i>>) -> Result<(), PrinterError> where W: fmt::Write { +impl<'a, 'i, T> ToCssWithContext<'a, 'i, T> for PseudoClass<'i> { + fn to_css_with_context(&self, dest: &mut Printer, context: Option<&StyleContext<'a, 'i, T>>) -> Result<(), PrinterError> where W: fmt::Write { use PseudoClass::*; match &self { Lang(lang) => { @@ -658,8 +658,8 @@ impl<'i> PseudoElement<'i> { } } -impl<'a, 'i> ToCssWithContext<'a, 'i> for SelectorList<'i, Selectors> { - fn to_css_with_context(&self, dest: &mut Printer, context: Option<&StyleContext<'a, 'i>>)-> Result<(), PrinterError> where W: fmt::Write { +impl<'a, 'i, T> ToCssWithContext<'a, 'i, T> for SelectorList<'i, Selectors> { + fn to_css_with_context(&self, dest: &mut Printer, context: Option<&StyleContext<'a, 'i, T>>)-> Result<(), PrinterError> where W: fmt::Write { serialize_selector_list(self.0.iter(), dest, context) } } @@ -680,8 +680,8 @@ impl ToCss for Combinator { } // Copied from the selectors crate and modified to override to_css implementation. -impl<'a, 'i> ToCssWithContext<'a, 'i> for parcel_selectors::parser::Selector<'i, Selectors> { - fn to_css_with_context(&self, dest: &mut Printer, context: Option<&StyleContext<'a, 'i>>)-> Result<(), PrinterError> +impl<'a, 'i, T> ToCssWithContext<'a, 'i, T> for parcel_selectors::parser::Selector<'i, Selectors> { + fn to_css_with_context(&self, dest: &mut Printer, context: Option<&StyleContext<'a, 'i, T>>)-> Result<(), PrinterError> where W: fmt::Write, { @@ -841,8 +841,8 @@ impl<'a, 'i> ToCssWithContext<'a, 'i> for parcel_selectors::parser::Selector<'i, } } -impl<'a, 'i> ToCssWithContext<'a, 'i> for Component<'i, Selectors> { - fn to_css_with_context(&self, dest: &mut Printer, context: Option<&StyleContext<'a, 'i>>)-> Result<(), PrinterError> where W: fmt::Write { +impl<'a, 'i, T> ToCssWithContext<'a, 'i, T> for Component<'i, Selectors> { + fn to_css_with_context(&self, dest: &mut Printer, context: Option<&StyleContext<'a, 'i, T>>)-> Result<(), PrinterError> where W: fmt::Write { use Component::*; match &self { Combinator(ref c) => c.to_css(dest), @@ -922,7 +922,7 @@ impl<'a, 'i> ToCssWithContext<'a, 'i> for Component<'i, Selectors> { } } -fn serialize_nesting(dest: &mut Printer, context: Option<&StyleContext>, first: bool)-> Result<(), PrinterError> where W: fmt::Write { +fn serialize_nesting(dest: &mut Printer, context: Option<&StyleContext>, first: bool)-> Result<(), PrinterError> where W: fmt::Write { if let Some(ctx) = context { // If there's only one simple selector, just serialize it directly. // Otherwise, use an :is() pseudo class. @@ -978,7 +978,7 @@ fn is_namespace(component: Option<&Component>) -> bool { ) } -fn serialize_selector_list<'a, 'i: 'a, I, W>(iter: I, dest: &mut Printer, context: Option<&StyleContext<'_, 'i>>)-> Result<(), PrinterError> +fn serialize_selector_list<'a, 'i: 'a, I, W, T>(iter: I, dest: &mut Printer, context: Option<&StyleContext<'_, 'i, T>>)-> Result<(), PrinterError> where I: Iterator>, W: fmt::Write, diff --git a/src/stylesheet.rs b/src/stylesheet.rs index e679d215..83a03f9e 100644 --- a/src/stylesheet.rs +++ b/src/stylesheet.rs @@ -1,4 +1,4 @@ -use cssparser::{Parser, ParserInput, RuleListParser}; +use cssparser::{Parser, ParserInput, RuleListParser, AtRuleParser}; use parcel_sourcemap::SourceMap; use crate::rules::{CssRule, CssRuleList, MinifyContext}; use crate::parser::TopLevelRuleParser; @@ -17,10 +17,10 @@ pub use crate::parser::ParserOptions; pub use crate::printer::PseudoClasses; #[derive(Debug)] -pub struct StyleSheet<'i> { - pub rules: CssRuleList<'i>, +pub struct StyleSheet<'i, T, R> { + pub rules: CssRuleList<'i, R>, pub sources: Vec, - options: ParserOptions + options: ParserOptions } #[derive(Default)] @@ -44,8 +44,8 @@ pub struct ToCssResult { pub dependencies: Option> } -impl<'i> StyleSheet<'i> { - pub fn new(sources: Vec, rules: CssRuleList, options: ParserOptions) -> StyleSheet { +impl<'i, T: AtRuleParser<'i>> StyleSheet<'i, T, T::AtRule> where T::AtRule: cssparser::ToCss { + pub fn new(sources: Vec, rules: CssRuleList<'i, T::AtRule>, options: ParserOptions) -> Self { StyleSheet { sources, rules, @@ -53,10 +53,10 @@ impl<'i> StyleSheet<'i> { } } - pub fn parse(filename: String, code: &'i str, options: ParserOptions) -> Result, Error>> { + pub fn parse(filename: String, code: &'i str, mut options: ParserOptions) -> Result>> { let mut input = ParserInput::new(&code); let mut parser = Parser::new(&mut input); - let rule_list_parser = RuleListParser::new_for_stylesheet(&mut parser, TopLevelRuleParser::new(&options)); + let rule_list_parser = RuleListParser::new_for_stylesheet(&mut parser, TopLevelRuleParser::new(&mut options)); let mut rules = vec![]; for rule in rule_list_parser { From 4a075b128e2935224f77873db82ae31b8898bee1 Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Sat, 5 Nov 2022 19:42:08 -0700 Subject: [PATCH 02/16] Experimenting with a visitor api --- examples/custom_at_rule.rs | 179 +++++++++++++++++++++++-------------- src/declaration.rs | 8 ++ src/rules/container.rs | 8 +- src/rules/document.rs | 8 +- src/rules/layer.rs | 8 +- src/rules/media.rs | 8 +- src/rules/mod.rs | 26 +++++- src/rules/nesting.rs | 8 ++ src/rules/style.rs | 10 ++- src/rules/supports.rs | 8 +- src/selector.rs | 8 ++ src/traits.rs | 13 +++ 12 files changed, 220 insertions(+), 72 deletions(-) diff --git a/examples/custom_at_rule.rs b/examples/custom_at_rule.rs index 14af9dfd..9a2343b2 100644 --- a/examples/custom_at_rule.rs +++ b/examples/custom_at_rule.rs @@ -1,9 +1,21 @@ +use std::collections::HashMap; + use cssparser::*; use lightningcss::{ - stylesheet::{StyleSheet, ParserOptions, PrinterOptions}, - values::color::CssColor, - properties::Property, printer::Printer, error::PrinterError, traits::ToCss + declaration::DeclarationBlock, + error::PrinterError, + printer::Printer, + rules::{style::StyleRule, CssRule, CssRuleList, Location}, + stylesheet::{ParserOptions, PrinterOptions, StyleSheet}, + targets::Browsers, + traits::{ToCss, VisitChildren, Visitor}, + vendor_prefix::VendorPrefix, +}; +use parcel_selectors::{ + parser::{Component, Selector}, + SelectorList, }; +use smallvec::smallvec; fn main() { let args: Vec = std::env::args().collect(); @@ -19,52 +31,63 @@ fn main() { source_index: 0, }; - let stylesheet = StyleSheet::parse( - &source, - opts - ).unwrap(); + let mut stylesheet = StyleSheet::parse(&source, opts).unwrap(); println!("{:?}", stylesheet); - - let result = stylesheet.to_css(PrinterOptions::default()).unwrap(); + + let mut style_rules = HashMap::new(); + stylesheet.rules.visit_children(&mut StyleRuleCollector { + rules: &mut style_rules, + }); + println!("{:?}", style_rules); + stylesheet.rules.visit_children(&mut ApplyVisitor { rules: &style_rules }); + + let result = stylesheet + .to_css(PrinterOptions { + targets: Some(Browsers { + chrome: Some(100 << 16), + ..Browsers::default() + }), + ..PrinterOptions::default() + }) + .unwrap(); println!("{}", result.code); } /// An @tailwind directive. -#[derive(Debug)] +#[derive(Debug, Clone)] enum TailwindDirective { Base, Components, Utilities, - Variants + Variants, } /// A custom at rule prelude. enum Prelude { Tailwind(TailwindDirective), - Apply(Vec, bool) + Apply(Vec), } /// A @tailwind rule. -#[derive(Debug)] +#[derive(Debug, Clone)] struct TailwindRule { directive: TailwindDirective, - loc: SourceLocation + loc: SourceLocation, } /// An @apply rule. -#[derive(Debug)] +#[derive(Debug, Clone)] struct ApplyRule { names: Vec, - important: bool, - loc: SourceLocation + loc: SourceLocation, } /// A custom at rule. -#[derive(Debug)] +#[derive(Debug, Clone)] enum AtRule { Tailwind(TailwindRule), - Apply(ApplyRule) + Apply(ApplyRule), } #[derive(Debug)] @@ -104,43 +127,90 @@ impl<'i> AtRuleParser<'i> for TailwindAtRuleParser { } } - let important = input.try_parse(|input| { - input.expect_delim('!')?; - input.expect_ident_matching("important") - }).is_ok(); - - Ok(Prelude::Apply(names, important)) + Ok(Prelude::Apply(names)) }, _ => Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name))) } } - fn rule_without_block( - &mut self, - prelude: Self::Prelude, - start: &ParserState, - ) -> Result { + fn rule_without_block(&mut self, prelude: Self::Prelude, start: &ParserState) -> Result { let loc = start.source_location(); match prelude { - Prelude::Tailwind(directive) => { - Ok(AtRule::Tailwind(TailwindRule { - directive, - loc - })) + Prelude::Tailwind(directive) => Ok(AtRule::Tailwind(TailwindRule { directive, loc })), + Prelude::Apply(names) => Ok(AtRule::Apply(ApplyRule { names, loc })), + } + } +} + +struct StyleRuleCollector<'i, 'a> { + rules: &'a mut HashMap>, +} + +impl<'i, 'a> Visitor<'i, AtRule> for StyleRuleCollector<'i, 'a> { + fn visit_rule(&mut self, rule: &mut lightningcss::rules::CssRule<'i, AtRule>) { + match rule { + CssRule::Style(rule) => { + for selector in rule.selectors.0.iter() { + if selector.len() != 1 { + continue; // TODO + } + for component in selector.iter_raw_match_order() { + match component { + Component::Class(name) => { + self.rules.insert(name.0.to_string(), rule.declarations.clone()); + } + _ => {} + } + } + } } - Prelude::Apply(names, important) => { - Ok(AtRule::Apply(ApplyRule { - names, - important, - loc - })) + _ => {} + } + + rule.visit_children(self) + } +} + +struct ApplyVisitor<'a, 'i> { + rules: &'a HashMap>, +} + +impl<'a, 'i> Visitor<'i, AtRule> for ApplyVisitor<'a, 'i> { + fn visit_rule(&mut self, rule: &mut CssRule<'i, AtRule>) { + // Replace @apply rule with nested style rule. + if let CssRule::Custom(AtRule::Apply(apply)) = rule { + let mut declarations = DeclarationBlock::new(); + for name in &apply.names { + let applied = self.rules.get(name).unwrap(); + declarations + .important_declarations + .extend(applied.important_declarations.iter().cloned()); + declarations.declarations.extend(applied.declarations.iter().cloned()); } + *rule = CssRule::Style(StyleRule { + // TODO expose nicer API for building selectors. + selectors: SelectorList(smallvec![Selector::from_vec2(vec![Component::Nesting])]), + vendor_prefix: VendorPrefix::None, + declarations, + rules: CssRuleList(vec![]), + loc: Location { + source_index: 0, + line: apply.loc.line, + column: apply.loc.column, + }, + }) } + + rule.visit_children(self) } } +impl<'i, V: Visitor<'i, AtRule>> VisitChildren<'i, AtRule, V> for AtRule { + fn visit_children(&mut self, _: &mut V) {} +} + impl ToCss for AtRule { - fn to_css(&self, dest: &mut Printer)-> Result<(), PrinterError> { + fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> { match self { AtRule::Tailwind(rule) => { let _ = rule.loc; // TODO: source maps @@ -148,32 +218,11 @@ impl ToCss for AtRule { TailwindDirective::Base => "TAILWIND BASE HERE", TailwindDirective::Components => "TAILWIND COMPONENTS HERE", TailwindDirective::Utilities => "TAILWIND UTILITIES HERE", - TailwindDirective::Variants => "TAILWIND VARIANTS HERE" + TailwindDirective::Variants => "TAILWIND VARIANTS HERE", }; dest.write_str(directive) } - AtRule::Apply(rule) => { - // TODO: possibly better to expose a way to transform these rules to inline the - // declarations rather than doing it during printing. - let _ = rule.loc; // TODO: source maps - for name in &rule.names { - match name.as_ref() { - "bg-blue-400" => { - let color = RGBA { red: 0, green: 0, blue: 255, alpha: 255 }; - let property = Property::BackgroundColor(CssColor::RGBA(color)); - property.to_css(dest, rule.important)?; - } - "text-red-400" => { - let color = RGBA { red: 255, green: 0, blue: 0, alpha: 255 }; - let property = Property::Color(CssColor::RGBA(color)); - property.to_css(dest, rule.important)?; - } - _ => {} - } - dest.write_str("; ")?; - } - Ok(()) - } + AtRule::Apply(_) => Ok(()), } } } diff --git a/src/declaration.rs b/src/declaration.rs index 575ffcb0..2de38218 100644 --- a/src/declaration.rs +++ b/src/declaration.rs @@ -94,6 +94,14 @@ impl<'i> DeclarationBlock<'i> { parser.expect_exhausted()?; Ok(result) } + + /// Returns an empty declaration block. + pub fn new() -> Self { + Self { + declarations: vec![], + important_declarations: vec![] + } + } } impl<'i> ToCss for DeclarationBlock<'i> { diff --git a/src/rules/container.rs b/src/rules/container.rs index 3ef2909a..ff4627ec 100644 --- a/src/rules/container.rs +++ b/src/rules/container.rs @@ -8,7 +8,7 @@ use crate::error::{MinifyError, ParserError, PrinterError}; use crate::media_query::MediaCondition; use crate::printer::Printer; use crate::rules::{StyleContext, ToCssWithContext}; -use crate::traits::{Parse, ToCss}; +use crate::traits::{Parse, ToCss, VisitChildren, Visitor}; use crate::values::ident::CustomIdent; /// A [@container](https://drafts.csswg.org/css-contain-3/#container-rule) rule. @@ -50,6 +50,12 @@ impl<'i> ToCss for ContainerName<'i> { } } +impl<'i, T: VisitChildren<'i, T, V>, V: Visitor<'i, T>> VisitChildren<'i, T, V> for ContainerRule<'i, T> { + fn visit_children(&mut self, visitor: &mut V) { + self.rules.visit_children(visitor) + } +} + impl<'i, T> ContainerRule<'i, T> { pub(crate) fn minify( &mut self, diff --git a/src/rules/document.rs b/src/rules/document.rs index d858dfc4..52216448 100644 --- a/src/rules/document.rs +++ b/src/rules/document.rs @@ -4,7 +4,7 @@ use super::Location; use super::{CssRuleList, MinifyContext}; use crate::error::{MinifyError, PrinterError}; use crate::printer::Printer; -use crate::traits::ToCss; +use crate::traits::{ToCss, Visitor, VisitChildren}; /// A [@-moz-document](https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#at-document) rule. /// @@ -26,6 +26,12 @@ impl<'i, T> MozDocumentRule<'i, T> { } } +impl<'i, T: VisitChildren<'i, T, V>, V: Visitor<'i, T>> VisitChildren<'i, T, V> for MozDocumentRule<'i, T> { + fn visit_children(&mut self, visitor: &mut V) { + self.rules.visit_children(visitor) + } +} + impl<'i, T: ToCss> ToCss for MozDocumentRule<'i, T> { fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> where diff --git a/src/rules/layer.rs b/src/rules/layer.rs index 0123907b..82d61a2f 100644 --- a/src/rules/layer.rs +++ b/src/rules/layer.rs @@ -3,7 +3,7 @@ use super::{CssRuleList, Location, MinifyContext}; use crate::error::{MinifyError, ParserError, PrinterError}; use crate::printer::Printer; -use crate::traits::{Parse, ToCss}; +use crate::traits::{Parse, ToCss, Visitor, VisitChildren}; use crate::values::string::CowArcStr; use cssparser::*; use smallvec::SmallVec; @@ -125,6 +125,12 @@ impl<'i, T> LayerBlockRule<'i, T> { } } +impl<'i, T: VisitChildren<'i, T, V>, V: Visitor<'i, T>> VisitChildren<'i, T, V> for LayerBlockRule<'i, T> { + fn visit_children(&mut self, visitor: &mut V) { + self.rules.visit_children(visitor) + } +} + impl<'i, T: ToCss> ToCss for LayerBlockRule<'i, T> { fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> where diff --git a/src/rules/media.rs b/src/rules/media.rs index 5ec5c35a..7691b051 100644 --- a/src/rules/media.rs +++ b/src/rules/media.rs @@ -6,7 +6,7 @@ use crate::error::{MinifyError, PrinterError}; use crate::media_query::MediaList; use crate::printer::Printer; use crate::rules::{StyleContext, ToCssWithContext}; -use crate::traits::ToCss; +use crate::traits::{ToCss, Visitor, VisitChildren}; /// A [@media](https://drafts.csswg.org/css-conditional-3/#at-media) rule. #[derive(Debug, PartialEq, Clone)] @@ -37,6 +37,12 @@ impl<'i, T> MediaRule<'i, T> { } } +impl<'i, T: VisitChildren<'i, T, V>, V: Visitor<'i, T>> VisitChildren<'i, T, V> for MediaRule<'i, T> { + fn visit_children(&mut self, visitor: &mut V) { + self.rules.visit_children(visitor) + } +} + impl<'a, 'i, T: ToCss> ToCssWithContext<'a, 'i, T> for MediaRule<'i, T> { fn to_css_with_context( &self, diff --git a/src/rules/mod.rs b/src/rules/mod.rs index fc0edb9d..6fdb2b35 100644 --- a/src/rules/mod.rs +++ b/src/rules/mod.rs @@ -69,7 +69,7 @@ use crate::rules::keyframes::KeyframesName; use crate::selector::{downlevel_selectors, get_prefix, is_equivalent}; use crate::stylesheet::ParserOptions; use crate::targets::Browsers; -use crate::traits::ToCss; +use crate::traits::{ToCss, VisitChildren, Visitor}; use crate::values::string::CowArcStr; use crate::vendor_prefix::VendorPrefix; use container::ContainerRule; @@ -252,6 +252,30 @@ pub(crate) struct MinifyContext<'a, 'i> { pub css_modules: bool, } +impl<'i, T: VisitChildren<'i, T, V>, V: Visitor<'i, T>> VisitChildren<'i, T, V> for CssRuleList<'i, T> { + fn visit_children(&mut self, visitor: &mut V) { + for rule in self.0.iter_mut() { + visitor.visit_rule(rule); + } + } +} + +impl<'i, T: VisitChildren<'i, T, V>, V: Visitor<'i, T>> VisitChildren<'i, T, V> for CssRule<'i, T> { + fn visit_children(&mut self, visitor: &mut V) { + match self { + CssRule::Media(media) => media.visit_children(visitor), + CssRule::Style(style) => style.visit_children(visitor), + CssRule::Supports(supports) => supports.visit_children(visitor), + CssRule::MozDocument(document) => document.visit_children(visitor), + CssRule::Nesting(nesting) => nesting.visit_children(visitor), + CssRule::LayerBlock(layer) => layer.visit_children(visitor), + CssRule::Container(container) => container.visit_children(visitor), + CssRule::Custom(rule) => rule.visit_children(visitor), + _ => {} + } + } +} + impl<'i, T> CssRuleList<'i, T> { pub(crate) fn minify( &mut self, diff --git a/src/rules/nesting.rs b/src/rules/nesting.rs index cadb2943..f9d2014b 100644 --- a/src/rules/nesting.rs +++ b/src/rules/nesting.rs @@ -7,6 +7,8 @@ use crate::error::{MinifyError, PrinterError}; use crate::printer::Printer; use crate::rules::{StyleContext, ToCssWithContext}; use crate::traits::ToCss; +use crate::traits::VisitChildren; +use crate::traits::Visitor; /// A [@nest](https://www.w3.org/TR/css-nesting-1/#at-nest) rule. #[derive(Debug, PartialEq, Clone)] @@ -29,6 +31,12 @@ impl<'i, T> NestingRule<'i, T> { } } +impl<'i, T: VisitChildren<'i, T, V>, V: Visitor<'i, T>> VisitChildren<'i, T, V> for NestingRule<'i, T> { + fn visit_children(&mut self, visitor: &mut V) { + self.style.visit_children(visitor) + } +} + impl<'a, 'i, T: ToCss> ToCssWithContext<'a, 'i, T> for NestingRule<'i, T> { fn to_css_with_context( &self, diff --git a/src/rules/style.rs b/src/rules/style.rs index 0bb6e291..7234b379 100644 --- a/src/rules/style.rs +++ b/src/rules/style.rs @@ -14,6 +14,8 @@ use crate::rules::{CssRuleList, StyleContext, ToCssWithContext}; use crate::selector::{is_compatible, is_unused, Selectors}; use crate::targets::Browsers; use crate::traits::ToCss; +use crate::traits::VisitChildren; +use crate::traits::Visitor; use crate::vendor_prefix::VendorPrefix; use cssparser::*; use parcel_selectors::SelectorList; @@ -37,7 +39,7 @@ pub struct StyleRule<'i, T> { pub selectors: SelectorList<'i, Selectors>, /// A vendor prefix override, used during selector printing. #[cfg_attr(feature = "serde", serde(skip, default = "VendorPrefix::empty"))] - pub(crate) vendor_prefix: VendorPrefix, + pub vendor_prefix: VendorPrefix, /// The declarations within the style rule. pub declarations: DeclarationBlock<'i>, /// Nested rules within the style rule. @@ -46,6 +48,12 @@ pub struct StyleRule<'i, T> { pub loc: Location, } +impl<'i, T: VisitChildren<'i, T, V>, V: Visitor<'i, T>> VisitChildren<'i, T, V> for StyleRule<'i, T> { + fn visit_children(&mut self, visitor: &mut V) { + self.rules.visit_children(visitor) + } +} + impl<'i, T> StyleRule<'i, T> { pub(crate) fn minify( &mut self, diff --git a/src/rules/supports.rs b/src/rules/supports.rs index 776df14d..4f35e619 100644 --- a/src/rules/supports.rs +++ b/src/rules/supports.rs @@ -5,7 +5,7 @@ use super::{CssRuleList, MinifyContext}; use crate::error::{MinifyError, ParserError, PrinterError}; use crate::printer::Printer; use crate::rules::{StyleContext, ToCssWithContext}; -use crate::traits::{Parse, ToCss}; +use crate::traits::{Parse, ToCss, Visitor, VisitChildren}; use crate::values::string::CowArcStr; use cssparser::*; @@ -32,6 +32,12 @@ impl<'i, T> SupportsRule<'i, T> { } } +impl<'i, T: VisitChildren<'i, T, V>, V: Visitor<'i, T>> VisitChildren<'i, T, V> for SupportsRule<'i, T> { + fn visit_children(&mut self, visitor: &mut V) { + self.rules.visit_children(visitor) + } +} + impl<'a, 'i, T: ToCss> ToCssWithContext<'a, 'i, T> for SupportsRule<'i, T> { fn to_css_with_context( &self, diff --git a/src/selector.rs b/src/selector.rs index f63b0757..f52d6d6e 100644 --- a/src/selector.rs +++ b/src/selector.rs @@ -913,6 +913,14 @@ impl<'a, 'i, T> ToCssWithContext<'a, 'i, T> for SelectorList<'i, Selectors> { } } +impl<'i> ToCss for SelectorList<'i, Selectors> { + fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> + where + W: std::fmt::Write { + serialize_selector_list::<_, _, DefaultAtRule>(self.0.iter(), dest, None, false) + } +} + impl ToCss for Combinator { fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> where diff --git a/src/traits.rs b/src/traits.rs index 5448116e..da2db03a 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -5,6 +5,7 @@ use crate::declaration::{DeclarationBlock, DeclarationList}; use crate::error::{ParserError, PrinterError}; use crate::printer::Printer; use crate::properties::{Property, PropertyId}; +use crate::rules::CssRule; use crate::stylesheet::{ParserOptions, PrinterOptions}; use crate::targets::Browsers; use crate::vendor_prefix::VendorPrefix; @@ -95,6 +96,18 @@ pub(crate) mod private { } } +/// A trait for visiting or transforming rules. +pub trait Visitor<'i, T> { + /// Visits a rule. + fn visit_rule(&mut self, rule: &mut CssRule<'i, T>); +} + +/// A trait for visiting the children of a rule. +pub trait VisitChildren<'i, T, V: Visitor<'i, T>> { + /// Visit the children of this rule. + fn visit_children(&mut self, visitor: &mut V); +} + pub(crate) trait FromStandard: Sized { fn from_standard(val: &T) -> Option; } From 21b66055975fbf5713bf35a0b4afcf3793ebec86 Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Sat, 5 Nov 2022 20:13:06 -0700 Subject: [PATCH 03/16] Fix serde --- src/parser.rs | 8 +++++--- src/selector.rs | 2 +- src/stylesheet.rs | 26 ++++++++++++++++++++------ 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index 372e4369..8c09c11a 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -36,8 +36,8 @@ use std::collections::HashMap; use std::sync::{Arc, RwLock}; /// CSS parsing options. -#[derive(Clone, Debug)] -pub struct ParserOptions<'o, 'i, T> { +#[derive(Clone, Debug, Default)] +pub struct ParserOptions<'o, 'i, T = DefaultAtRuleParser> { /// Filename to use in error messages. pub filename: String, /// Whether the enable the [CSS nesting](https://www.w3.org/TR/css-nesting-1/) draft syntax. @@ -89,7 +89,7 @@ impl<'o, 'i> ParserOptions<'o, 'i, DefaultAtRuleParser> { } } -#[derive(Clone)] +#[derive(Clone, Default)] pub struct DefaultAtRuleParser; impl<'i> AtRuleParser<'i> for DefaultAtRuleParser { type AtRule = DefaultAtRule; @@ -97,6 +97,8 @@ impl<'i> AtRuleParser<'i> for DefaultAtRuleParser { type Prelude = (); } +#[derive(PartialEq, Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct DefaultAtRule; impl crate::traits::ToCss for DefaultAtRule { fn to_css(&self, _: &mut Printer) -> Result<(), PrinterError> { diff --git a/src/selector.rs b/src/selector.rs index f52d6d6e..e75a580a 100644 --- a/src/selector.rs +++ b/src/selector.rs @@ -1695,7 +1695,7 @@ where .map(|selector| { let mut dest = String::new(); let mut printer = Printer::new(&mut dest, PrinterOptions::default()); - serialize_selector(selector, &mut printer, None, false).unwrap(); + serialize_selector::<_, DefaultAtRule>(selector, &mut printer, None, false).unwrap(); dest }) .collect::>() diff --git a/src/stylesheet.rs b/src/stylesheet.rs index 53c4a182..8b3306f0 100644 --- a/src/stylesheet.rs +++ b/src/stylesheet.rs @@ -9,12 +9,12 @@ use crate::css_modules::{CssModule, CssModuleExports, CssModuleReferences}; use crate::declaration::{DeclarationBlock, DeclarationHandler}; use crate::dependencies::Dependency; use crate::error::{Error, ErrorLocation, MinifyErrorKind, ParserError, PrinterError, PrinterErrorKind}; -use crate::parser::{TopLevelRuleParser, DefaultAtRuleParser}; +use crate::parser::{DefaultAtRuleParser, TopLevelRuleParser}; use crate::printer::Printer; use crate::rules::{CssRule, CssRuleList, MinifyContext}; use crate::targets::Browsers; use crate::traits::ToCss; -use cssparser::{Parser, ParserInput, RuleListParser, AtRuleParser}; +use cssparser::{AtRuleParser, Parser, ParserInput, RuleListParser}; use parcel_sourcemap::SourceMap; use std::collections::{HashMap, HashSet}; @@ -60,7 +60,16 @@ pub use crate::printer::PseudoClasses; #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct StyleSheet<'i, 'o, T: AtRuleParser<'i> = DefaultAtRuleParser> { /// A list of top-level rules within the style sheet. - #[cfg_attr(feature = "serde", serde(borrow))] + #[cfg_attr( + feature = "serde", + serde( + borrow, + bound( + serialize = "T::AtRule: serde::Serialize", + deserialize = "T::AtRule: serde::Deserialize<'de>" + ) + ) + )] pub rules: CssRuleList<'i, T::AtRule>, /// A list of file names for all source files included within the style sheet. /// Sources are referenced by index in the `loc` property of each rule. @@ -102,10 +111,14 @@ pub struct ToCssResult { impl<'i, 'o, T: AtRuleParser<'i>> StyleSheet<'i, 'o, T> where - T::AtRule: ToCss + T::AtRule: ToCss, { /// Creates a new style sheet with the given source filenames and rules. - pub fn new(sources: Vec, rules: CssRuleList<'i, T::AtRule>, options: ParserOptions<'o, 'i, T>) -> StyleSheet<'i, 'o, T> { + pub fn new( + sources: Vec, + rules: CssRuleList<'i, T::AtRule>, + options: ParserOptions<'o, 'i, T>, + ) -> StyleSheet<'i, 'o, T> { StyleSheet { sources, source_map_urls: Vec::new(), @@ -118,7 +131,8 @@ where pub fn parse(code: &'i str, mut options: ParserOptions<'o, 'i, T>) -> Result>> { let mut input = ParserInput::new(&code); let mut parser = Parser::new(&mut input); - let mut rule_list_parser = RuleListParser::new_for_stylesheet(&mut parser, TopLevelRuleParser::new(&mut options)); + let mut rule_list_parser = + RuleListParser::new_for_stylesheet(&mut parser, TopLevelRuleParser::new(&mut options)); let mut rules = vec![]; while let Some(rule) = rule_list_parser.next() { From 305733f2d76500788183f21ba85081f35df3441b Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Thu, 24 Nov 2022 12:33:18 -0500 Subject: [PATCH 04/16] Derive visit trait automatically --- Cargo.lock | 10 ++ Cargo.toml | 4 +- derive/Cargo.toml | 14 ++ derive/src/lib.rs | 202 +++++++++++++++++++++++++++ examples/custom_at_rule.rs | 39 +++++- src/declaration.rs | 5 +- src/dependencies.rs | 3 +- src/lib.rs | 11 +- src/macros.rs | 8 +- src/media_query.rs | 19 ++- src/properties/align.rs | 19 +-- src/properties/animation.rs | 5 +- src/properties/background.rs | 7 +- src/properties/border.rs | 5 +- src/properties/border_image.rs | 7 +- src/properties/border_radius.rs | 1 + src/properties/box_shadow.rs | 3 +- src/properties/contain.rs | 4 +- src/properties/css_modules.rs | 5 +- src/properties/custom.rs | 19 ++- src/properties/display.rs | 7 +- src/properties/effects.rs | 7 +- src/properties/flex.rs | 1 + src/properties/font.rs | 17 +-- src/properties/grid.rs | 26 ++-- src/properties/list.rs | 7 +- src/properties/margin_padding.rs | 1 + src/properties/masking.rs | 5 +- src/properties/mod.rs | 6 +- src/properties/outline.rs | 3 +- src/properties/overflow.rs | 1 + src/properties/position.rs | 5 +- src/properties/size.rs | 5 +- src/properties/svg.rs | 9 +- src/properties/text.rs | 17 ++- src/properties/transform.rs | 17 +-- src/properties/transition.rs | 1 + src/properties/ui.rs | 9 +- src/rules/container.rs | 18 +-- src/rules/counter_style.rs | 4 +- src/rules/custom_media.rs | 4 +- src/rules/document.rs | 16 +-- src/rules/font_face.rs | 14 +- src/rules/font_palette_values.rs | 10 +- src/rules/import.rs | 6 +- src/rules/keyframes.rs | 11 +- src/rules/layer.rs | 21 ++- src/rules/media.rs | 16 +-- src/rules/mod.rs | 66 ++++----- src/rules/namespace.rs | 6 +- src/rules/nesting.rs | 17 +-- src/rules/page.rs | 5 +- src/rules/property.rs | 7 +- src/rules/style.rs | 18 +-- src/rules/supports.rs | 23 ++-- src/rules/unknown.rs | 5 +- src/rules/viewport.rs | 5 +- src/selector.rs | 9 +- src/traits.rs | 13 -- src/values/alpha.rs | 3 +- src/values/angle.rs | 4 +- src/values/calc.rs | 8 +- src/values/color.rs | 17 ++- src/values/easing.rs | 5 +- src/values/gradient.rs | 29 ++-- src/values/ident.rs | 9 +- src/values/image.rs | 9 +- src/values/length.rs | 11 +- src/values/percentage.rs | 8 +- src/values/position.rs | 5 +- src/values/ratio.rs | 4 +- src/values/rect.rs | 3 +- src/values/resolution.rs | 4 +- src/values/shape.rs | 15 +- src/values/size.rs | 3 +- src/values/string.rs | 10 +- src/values/time.rs | 4 +- src/values/url.rs | 4 +- src/vendor_prefix.rs | 6 + src/visitor.rs | 229 +++++++++++++++++++++++++++++++ 80 files changed, 901 insertions(+), 317 deletions(-) create mode 100644 derive/Cargo.toml create mode 100644 derive/src/lib.rs create mode 100644 src/visitor.rs diff --git a/Cargo.lock b/Cargo.lock index 7a481d43..8da4f1ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -679,6 +679,7 @@ dependencies = [ "itertools", "jemallocator", "lazy_static", + "lightningcss-derive", "parcel_selectors", "parcel_sourcemap", "pathdiff", @@ -689,6 +690,15 @@ dependencies = [ "smallvec", ] +[[package]] +name = "lightningcss-derive" +version = "1.0.0-alpha.35" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "lightningcss_c_bindings" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index d978e389..b07f6e08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,8 @@ members = [ "node", "selectors", - "c" + "c", + "derive" ] [package] @@ -43,6 +44,7 @@ browserslist-rs = { version = "0.7.0", optional = true } rayon = "1.5.1" dashmap = "5.0.0" serde_json = { version = "1.0.78", optional = true } +lightningcss-derive = { version = "1.0.0-alpha.35", path = "./derive" } [target.'cfg(target_os = "macos")'.dependencies] jemallocator = { version = "0.3.2", features = ["disable_initial_exec_tls"], optional = true } diff --git a/derive/Cargo.toml b/derive/Cargo.toml new file mode 100644 index 00000000..e1587f0c --- /dev/null +++ b/derive/Cargo.toml @@ -0,0 +1,14 @@ +[package] +authors = ["Devon Govett "] +name = "lightningcss-derive" +version = "1.0.0-alpha.35" +license = "MPL-2.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +syn = "1.0" +quote = "1.0" +proc-macro2 = "1.0" diff --git a/derive/src/lib.rs b/derive/src/lib.rs new file mode 100644 index 00000000..877c4bc0 --- /dev/null +++ b/derive/src/lib.rs @@ -0,0 +1,202 @@ +use std::collections::HashSet; + +use proc_macro::{self, TokenStream}; +use proc_macro2::Span; +use quote::quote; +use syn::{ + parse::Parse, parse_macro_input, parse_quote, Attribute, Data, DataEnum, DeriveInput, Field, Fields, + GenericParam, Ident, Member, Token, Type, +}; + +#[proc_macro_derive(Visit, attributes(visit, skip_visit, skip_type))] +pub fn derive_visit_children(input: TokenStream) -> TokenStream { + let DeriveInput { + ident, + data, + generics, + attrs, + .. + } = parse_macro_input!(input); + + let mut impl_generics = generics.clone(); + if impl_generics.lifetimes().next().is_none() { + impl_generics.params.insert(0, parse_quote! { 'i }) + } + + let t = impl_generics.type_params().find(|g| &g.ident.to_string() == "R"); + let t = if let Some(t) = t { + GenericParam::Type(t.clone()) + } else { + let t: GenericParam = parse_quote! { __T }; + impl_generics.params.push(t.clone()); + t + }; + + let lifetime = impl_generics.lifetimes().next().unwrap().clone(); + + let v = quote! { __V }; + impl_generics + .params + .push(parse_quote! { #v: crate::visitor::Visitor<#lifetime, #t> }); + + for ty in generics.type_params() { + impl_generics.make_where_clause().predicates.push(parse_quote! { + #ty: Visit<#lifetime, #t, #v> + }) + } + + let options = if let Some(attr) = attrs.iter().find(|attr| attr.path.is_ident("visit")) { + let opts: VisitOptions = attr.parse_args().unwrap(); + Some(opts) + } else { + None + }; + + let mut seen_types = HashSet::new(); + let mut child_types = Vec::new(); + let mut visit = Vec::new(); + match data { + Data::Struct(s) => { + for (index, Field { ty, ident, attrs, .. }) in s.fields.iter().enumerate() { + if attrs.iter().any(|attr| attr.path.is_ident("skip_visit")) { + continue; + } + + if matches!(ty, Type::Reference(_)) { + continue; + } + + if !seen_types.contains(ty) && !skip_type(attrs) { + seen_types.insert(ty.clone()); + child_types.push(quote! { + <#ty as Visit<#lifetime, #t, #v>>::CHILD_TYPES.bits() + }); + } + + let name = ident + .as_ref() + .map_or_else(|| Member::Unnamed(index.into()), |ident| Member::Named(ident.clone())); + visit.push(quote! { self.#name.visit(visitor); }) + } + } + Data::Enum(DataEnum { variants, .. }) => { + let variants = variants + .iter() + .map(|variant| { + let name = &variant.ident; + let mut field_names = Vec::new(); + let mut visit_fields = Vec::new(); + for (index, Field { ty, ident, attrs, .. }) in variant.fields.iter().enumerate() { + let name = ident.as_ref().map_or_else( + || Ident::new(&format!("_{}", index), Span::call_site()), + |ident| ident.clone(), + ); + field_names.push(name.clone()); + + if matches!(ty, Type::Reference(_)) { + continue; + } + + if !seen_types.contains(ty) && !skip_type(attrs) && !skip_type(&variant.attrs) { + seen_types.insert(ty.clone()); + child_types.push(quote! { + <#ty as Visit<#lifetime, #t, #v>>::CHILD_TYPES.bits() + }); + } + + visit_fields.push(quote! { #name.visit(visitor); }) + } + + match variant.fields { + Fields::Unnamed(_) => { + quote! { + Self::#name(#(#field_names),*) => { + #(#visit_fields)* + } + } + } + Fields::Named(_) => { + quote! { + Self::#name { #(#field_names),* } => { + #(#visit_fields)* + } + } + } + Fields::Unit => quote! {}, + } + }) + .collect::(); + + visit.push(quote! { + match self { + #variants + _ => {} + } + }) + } + _ => {} + } + + if child_types.is_empty() { + child_types.push(quote! { crate::visitor::VisitTypes::empty().bits() }); + } + + let self_visit = if let Some(VisitOptions { visit, kind }) = options { + child_types.push(quote! { crate::visitor::VisitTypes::#kind.bits() }); + + quote! { + fn visit(&mut self, visitor: &mut #v) { + if >::CHILD_TYPES.intersects(#v::TYPES) { + visitor.#visit(self) + } else { + self.visit_children(visitor) + } + } + } + } else { + quote! {} + }; + + let (_, ty_generics, _) = generics.split_for_impl(); + let (impl_generics, _, where_clause) = impl_generics.split_for_impl(); + + let child_types = quote! { + unsafe { crate::visitor::VisitTypes::from_bits_unchecked(#(#child_types)|*) } + }; + + let output = quote! { + impl #impl_generics Visit<#lifetime, #t, #v> for #ident #ty_generics #where_clause { + const CHILD_TYPES: crate::visitor::VisitTypes = #child_types; + + #self_visit + + fn visit_children(&mut self, visitor: &mut #v) { + if !>::CHILD_TYPES.intersects(#v::TYPES) { + return + } + + #(#visit)* + } + } + }; + + output.into() +} + +fn skip_type(attrs: &Vec) -> bool { + attrs.iter().any(|attr| attr.path.is_ident("skip_type")) +} + +struct VisitOptions { + visit: Ident, + kind: Ident, +} + +impl Parse for VisitOptions { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let visit: Ident = input.parse()?; + let _: Token![,] = input.parse()?; + let kind: Ident = input.parse()?; + Ok(Self { visit, kind }) + } +} diff --git a/examples/custom_at_rule.rs b/examples/custom_at_rule.rs index 9a2343b2..9cfc7522 100644 --- a/examples/custom_at_rule.rs +++ b/examples/custom_at_rule.rs @@ -8,8 +8,11 @@ use lightningcss::{ rules::{style::StyleRule, CssRule, CssRuleList, Location}, stylesheet::{ParserOptions, PrinterOptions, StyleSheet}, targets::Browsers, - traits::{ToCss, VisitChildren, Visitor}, + traits::ToCss, + values::length::LengthValue, vendor_prefix::VendorPrefix, + visit_types, + visitor::{Visit, VisitTypes, Visitor}, }; use parcel_selectors::{ parser::{Component, Selector}, @@ -147,6 +150,8 @@ struct StyleRuleCollector<'i, 'a> { } impl<'i, 'a> Visitor<'i, AtRule> for StyleRuleCollector<'i, 'a> { + const TYPES: VisitTypes = VisitTypes::RULES; + fn visit_rule(&mut self, rule: &mut lightningcss::rules::CssRule<'i, AtRule>) { match rule { CssRule::Style(rule) => { @@ -176,6 +181,8 @@ struct ApplyVisitor<'a, 'i> { } impl<'a, 'i> Visitor<'i, AtRule> for ApplyVisitor<'a, 'i> { + const TYPES: VisitTypes = visit_types!(RULES | COLORS | LENGTHS | DASHED_IDENTS); + fn visit_rule(&mut self, rule: &mut CssRule<'i, AtRule>) { // Replace @apply rule with nested style rule. if let CssRule::Custom(AtRule::Apply(apply)) = rule { @@ -203,9 +210,37 @@ impl<'a, 'i> Visitor<'i, AtRule> for ApplyVisitor<'a, 'i> { rule.visit_children(self) } + + // fn visit_property(&mut self, property: &mut lightningcss::properties::Property<'i>) { + // println!("VISIT PROPERTY {:?}", property); + // property.visit_children(self) + // } + + fn visit_url(&mut self, url: &mut lightningcss::values::url::Url<'i>) { + println!("VISIT URL {:?}", url); + + url.url = format!("https://mywebsite.com/{}", url.url).into() + } + + fn visit_color(&mut self, color: &mut lightningcss::values::color::CssColor) { + *color = color.to_lab() + } + + fn visit_length(&mut self, length: &mut lightningcss::values::length::LengthValue) { + match length { + LengthValue::Px(px) => *length = LengthValue::Rem(*px / 16.0), + _ => {} + } + } + + fn visit_dashed_ident(&mut self, ident: &mut lightningcss::values::ident::DashedIdent) { + ident.0 = format!("--prefix-{}", &ident.0[2..]).into() + } } -impl<'i, V: Visitor<'i, AtRule>> VisitChildren<'i, AtRule, V> for AtRule { +impl<'i, V: Visitor<'i, AtRule>> Visit<'i, AtRule, V> for AtRule { + const CHILD_TYPES: VisitTypes = VisitTypes::empty(); + fn visit_children(&mut self, _: &mut V) {} } diff --git a/src/declaration.rs b/src/declaration.rs index 2de38218..3c24e624 100644 --- a/src/declaration.rs +++ b/src/declaration.rs @@ -34,6 +34,7 @@ use crate::properties::{Property, PropertyId}; use crate::targets::Browsers; use crate::traits::{PropertyHandler, ToCss}; use crate::values::string::CowArcStr; +use crate::visitor::Visit; use cssparser::*; /// A CSS declaration block. @@ -41,7 +42,7 @@ use cssparser::*; /// Properties are separated into a list of `!important` declararations, /// and a list of normal declarations. This reduces memory usage compared /// with storing a boolean along with each property. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct DeclarationBlock<'i> { /// A list of `!important` declarations in the block. @@ -99,7 +100,7 @@ impl<'i> DeclarationBlock<'i> { pub fn new() -> Self { Self { declarations: vec![], - important_declarations: vec![] + important_declarations: vec![], } } } diff --git a/src/dependencies.rs b/src/dependencies.rs index 6f4397e6..af32687d 100644 --- a/src/dependencies.rs +++ b/src/dependencies.rs @@ -13,6 +13,7 @@ use crate::printer::PrinterOptions; use crate::rules::import::ImportRule; use crate::traits::ToCss; use crate::values::url::Url; +use crate::visitor::Visit; use cssparser::SourceLocation; use serde::Serialize; @@ -121,7 +122,7 @@ pub struct SourceRange { } /// A line and column position within a source file. -#[derive(Serialize, Debug, Clone, Copy, PartialEq)] +#[derive(Serialize, Debug, Clone, Copy, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Deserialize))] pub struct Location { /// The line number, starting from 1. diff --git a/src/lib.rs b/src/lib.rs index ffe8a90a..fee2ff3d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,6 +37,7 @@ pub mod targets; pub mod traits; pub mod values; pub mod vendor_prefix; +pub mod visitor; #[cfg(test)] mod tests { @@ -51,7 +52,7 @@ mod tests { use crate::targets::Browsers; use crate::traits::{Parse, ToCss}; use crate::values::color::CssColor; - use cssparser::{SourceLocation, AtRuleParser}; + use cssparser::{AtRuleParser, SourceLocation}; use indoc::indoc; use std::collections::HashMap; @@ -59,7 +60,13 @@ mod tests { test_with_options(source, expected, ParserOptions::default()) } - fn test_with_options<'i, 'o, T: AtRuleParser<'i>>(source: &'i str, expected: &'i str, options: ParserOptions<'o, 'i, T>) where T::AtRule: ToCss { + fn test_with_options<'i, 'o, T: AtRuleParser<'i>>( + source: &'i str, + expected: &'i str, + options: ParserOptions<'o, 'i, T>, + ) where + T::AtRule: ToCss, + { let mut stylesheet = StyleSheet::parse(&source, options).unwrap(); stylesheet.minify(MinifyOptions::default()).unwrap(); let res = stylesheet.to_css(PrinterOptions::default()).unwrap(); diff --git a/src/macros.rs b/src/macros.rs index f173bbfc..142ba01f 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -9,7 +9,7 @@ macro_rules! enum_property { } ) => { $(#[$outer])* - #[derive(Debug, Clone, Copy, PartialEq)] + #[derive(Debug, Clone, Copy, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(rename_all = "lowercase"))] $vis enum $name { $( @@ -66,7 +66,7 @@ macro_rules! enum_property { } ) => { $(#[$outer])* - #[derive(Debug, Clone, Copy, PartialEq)] + #[derive(Debug, Clone, Copy, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] $vis enum $name { $( @@ -339,7 +339,7 @@ macro_rules! define_shorthand { } ) => { $(#[$outer])* - #[derive(Debug, Clone, PartialEq)] + #[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct $name$(<$l>)? { $( @@ -553,7 +553,7 @@ macro_rules! define_list_shorthand { } ) => { $(#[$outer])* - #[derive(Debug, Clone, PartialEq)] + #[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct $name$(<$l>)? { $( diff --git a/src/media_query.rs b/src/media_query.rs index 1d471e75..9f6c1b18 100644 --- a/src/media_query.rs +++ b/src/media_query.rs @@ -10,11 +10,12 @@ use crate::traits::{Parse, ToCss}; use crate::values::number::CSSNumber; use crate::values::string::CowArcStr; use crate::values::{length::Length, ratio::Ratio, resolution::Resolution}; +use crate::visitor::Visit; use cssparser::*; use std::collections::{HashMap, HashSet}; /// A [media query list](https://drafts.csswg.org/mediaqueries/#mq-list). -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct MediaList<'i> { /// The list of media queries. @@ -141,7 +142,7 @@ enum_property! { } /// A [media type](https://drafts.csswg.org/mediaqueries/#media-types) within a media query. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -173,7 +174,8 @@ impl<'i> Parse<'i> for MediaType<'i> { } /// A [media query](https://drafts.csswg.org/mediaqueries/#media). -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Visit)] +#[visit(visit_media_query, MEDIA_QUERIES)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct MediaQuery<'i> { /// The qualifier for this query. @@ -362,7 +364,7 @@ enum_property! { } /// Represents a media condition. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -373,10 +375,13 @@ pub enum MediaCondition<'i> { #[cfg_attr(feature = "serde", serde(borrow))] Feature(MediaFeature<'i>), /// A negation of a condition. + #[skip_type] Not(Box>), /// A set of joint operations. + #[skip_type] Operation(Vec>, Operator), /// A condition wrapped in parenthesis. + #[skip_type] InParens(Box>), } @@ -474,7 +479,7 @@ impl<'i> ToCss for MediaCondition<'i> { } /// A [comparator](https://drafts.csswg.org/mediaqueries/#typedef-mf-comparison) within a media query. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -530,7 +535,7 @@ impl MediaFeatureComparison { } /// A [media feature](https://drafts.csswg.org/mediaqueries/#typedef-media-feature) -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -732,7 +737,7 @@ where /// [media feature value](https://drafts.csswg.org/mediaqueries/#typedef-mf-value) within a media query. /// /// See [MediaFeature](MediaFeature). -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), diff --git a/src/properties/align.rs b/src/properties/align.rs index 2b6f4c81..ef015549 100644 --- a/src/properties/align.rs +++ b/src/properties/align.rs @@ -13,11 +13,12 @@ use crate::targets::Browsers; use crate::traits::{FromStandard, Parse, PropertyHandler, Shorthand, ToCss}; use crate::values::length::LengthPercentage; use crate::vendor_prefix::VendorPrefix; +use crate::visitor::Visit; use cssparser::*; /// A [``](https://www.w3.org/TR/css-align-3/#typedef-baseline-position) value, /// as used in the alignment properties. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -106,7 +107,7 @@ enum_property! { } /// A value for the [align-content](https://www.w3.org/TR/css-align-3/#propdef-align-content) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -165,7 +166,7 @@ impl ToCss for AlignContent { } /// A value for the [justify-content](https://www.w3.org/TR/css-align-3/#propdef-justify-content) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -326,7 +327,7 @@ enum_property! { } /// A value for the [align-self](https://www.w3.org/TR/css-align-3/#align-self-property) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -392,7 +393,7 @@ impl ToCss for AlignSelf { } /// A value for the [justify-self](https://www.w3.org/TR/css-align-3/#justify-self-property) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -548,7 +549,7 @@ impl ToCss for PlaceSelf { } /// A value for the [align-items](https://www.w3.org/TR/css-align-3/#align-items-property) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -607,7 +608,7 @@ impl ToCss for AlignItems { } /// A legacy justification keyword, as used in the `justify-items` property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -673,7 +674,7 @@ impl ToCss for LegacyJustify { } /// A value for the [justify-items](https://www.w3.org/TR/css-align-3/#justify-items-property) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -828,7 +829,7 @@ impl ToCss for PlaceItems { /// A [gap](https://www.w3.org/TR/css-align-3/#column-row-gap) value, as used in the /// `column-gap` and `row-gap` properties. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), diff --git a/src/properties/animation.rs b/src/properties/animation.rs index 0217bd4e..b4aad1ee 100644 --- a/src/properties/animation.rs +++ b/src/properties/animation.rs @@ -12,12 +12,13 @@ use crate::traits::{Parse, PropertyHandler, Shorthand, ToCss, Zero}; use crate::values::number::CSSNumber; use crate::values::string::CowArcStr; use crate::values::{easing::EasingFunction, ident::CustomIdent, time::Time}; +use crate::visitor::Visit; use cssparser::*; use itertools::izip; use smallvec::SmallVec; /// A value for the [animation-name](https://drafts.csswg.org/css-animations/#animation-name) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -86,7 +87,7 @@ impl<'i> ToCss for AnimationName<'i> { pub type AnimationNameList<'i> = SmallVec<[AnimationName<'i>; 1]>; /// A value for the [animation-iteration-count](https://drafts.csswg.org/css-animations/#animation-iteration-count) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), diff --git a/src/properties/background.rs b/src/properties/background.rs index 277211f8..2000b03b 100644 --- a/src/properties/background.rs +++ b/src/properties/background.rs @@ -12,12 +12,13 @@ use crate::traits::{FallbackValues, Parse, PropertyHandler, Shorthand, ToCss}; use crate::values::color::ColorFallbackKind; use crate::values::image::ImageFallback; use crate::values::{color::CssColor, image::Image, length::LengthPercentageOrAuto, position::*}; +use crate::visitor::Visit; use cssparser::*; use itertools::izip; use smallvec::SmallVec; /// A value for the [background-size](https://www.w3.org/TR/css-backgrounds-3/#background-size) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -108,7 +109,7 @@ enum_property! { } /// A value for the [background-repeat](https://www.w3.org/TR/css-backgrounds-3/#background-repeat) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct BackgroundRepeat { /// A repeat style for the x direction. @@ -298,7 +299,7 @@ impl ToCss for BackgroundPosition { } /// A value for the [background](https://www.w3.org/TR/css-backgrounds-3/#background) shorthand property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Background<'i> { /// The background image. diff --git a/src/properties/border.rs b/src/properties/border.rs index b17200fc..e1e30fa2 100644 --- a/src/properties/border.rs +++ b/src/properties/border.rs @@ -17,10 +17,11 @@ use crate::values::color::{ColorFallbackKind, CssColor}; use crate::values::length::*; use crate::values::rect::Rect; use crate::values::size::Size2D; +use crate::visitor::Visit; use cssparser::*; /// A value for the [border-width](https://www.w3.org/TR/css-backgrounds-3/#border-width) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -107,7 +108,7 @@ impl Default for LineStyle { } /// A generic type that represents the `border` and `outline` shorthand properties. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct GenericBorder { /// The width of the border. diff --git a/src/properties/border_image.rs b/src/properties/border_image.rs index 4127498f..d32ab2dd 100644 --- a/src/properties/border_image.rs +++ b/src/properties/border_image.rs @@ -12,6 +12,7 @@ use crate::traits::{Parse, PropertyHandler, Shorthand, ToCss}; use crate::values::image::Image; use crate::values::number::CSSNumber; use crate::values::rect::Rect; +use crate::visitor::Visit; use crate::{ traits::FallbackValues, values::{ @@ -36,7 +37,7 @@ enum_property! { } /// A value for the [border-image-repeat](https://www.w3.org/TR/css-backgrounds-3/#border-image-repeat) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct BorderImageRepeat { /// The horizontal repeat value. @@ -80,7 +81,7 @@ impl ToCss for BorderImageRepeat { } /// A value for the [border-image-width](https://www.w3.org/TR/css-backgrounds-3/#border-image-width) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -134,7 +135,7 @@ impl ToCss for BorderImageSideWidth { } /// A value for the [border-image-slice](https://www.w3.org/TR/css-backgrounds-3/#border-image-slice) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct BorderImageSlice { /// The offsets from the edges of the image. diff --git a/src/properties/border_radius.rs b/src/properties/border_radius.rs index f00c0305..841a197a 100644 --- a/src/properties/border_radius.rs +++ b/src/properties/border_radius.rs @@ -14,6 +14,7 @@ use crate::traits::{Parse, PropertyHandler, Shorthand, ToCss, Zero}; use crate::values::length::*; use crate::values::rect::Rect; use crate::values::size::Size2D; +use crate::visitor::Visit; use cssparser::*; define_shorthand! { diff --git a/src/properties/box_shadow.rs b/src/properties/box_shadow.rs index 6d9dff76..c972b2fb 100644 --- a/src/properties/box_shadow.rs +++ b/src/properties/box_shadow.rs @@ -12,11 +12,12 @@ use crate::traits::{Parse, PropertyHandler, ToCss, Zero}; use crate::values::color::{ColorFallbackKind, CssColor}; use crate::values::length::Length; use crate::vendor_prefix::VendorPrefix; +use crate::visitor::Visit; use cssparser::*; use smallvec::SmallVec; /// A value for the [box-shadow](https://drafts.csswg.org/css-backgrounds/#box-shadow) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct BoxShadow { /// The color of the box shadow. diff --git a/src/properties/contain.rs b/src/properties/contain.rs index be397164..5d8816a6 100644 --- a/src/properties/contain.rs +++ b/src/properties/contain.rs @@ -16,6 +16,7 @@ use crate::{ rules::container::ContainerName as ContainerIdent, targets::Browsers, traits::{Parse, PropertyHandler, Shorthand, ToCss}, + visitor::Visit, }; bitflags! { @@ -23,6 +24,7 @@ bitflags! { /// Establishes the element as a query container for the purpose of container queries. /// /// `normal` is mutually exclusive, but other combinations of flags are allowed. + #[derive(Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ContainerType: u8 { /// The element is not a query container for any container size queries, @@ -86,7 +88,7 @@ impl ToCss for ContainerType { } /// A value for the [container-name](https://drafts.csswg.org/css-contain-3/#container-name) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), diff --git a/src/properties/css_modules.rs b/src/properties/css_modules.rs index b57e5f69..25d3a446 100644 --- a/src/properties/css_modules.rs +++ b/src/properties/css_modules.rs @@ -6,11 +6,12 @@ use crate::printer::Printer; use crate::traits::{Parse, ToCss}; use crate::values::ident::{CustomIdent, CustomIdentList}; use crate::values::string::CowArcStr; +use crate::visitor::Visit; use cssparser::*; use smallvec::SmallVec; /// A value for the [composes](https://github.com/css-modules/css-modules/#dependencies) property from CSS modules. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Composes<'i> { /// A list of class names to compose. @@ -25,7 +26,7 @@ pub struct Composes<'i> { /// Defines where the class names referenced in the `composes` property are located. /// /// See [Composes](Composes). -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), diff --git a/src/properties/custom.rs b/src/properties/custom.rs index 2796b0ae..a9ffbf7d 100644 --- a/src/properties/custom.rs +++ b/src/properties/custom.rs @@ -18,10 +18,11 @@ use crate::values::percentage::Percentage; use crate::values::string::CowArcStr; use crate::values::url::Url; use crate::vendor_prefix::VendorPrefix; +use crate::visitor::Visit; use cssparser::*; /// A CSS custom property, representing any unknown property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct CustomProperty<'i> { /// The name of the property. @@ -50,7 +51,7 @@ impl<'i> CustomProperty<'i> { /// This type is used when the value of a known property could not /// be parsed, e.g. in the case css `var()` references are encountered. /// In this case, the raw tokens are stored instead. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct UnparsedProperty<'i> { /// The id of the property. @@ -93,12 +94,12 @@ impl<'i> UnparsedProperty<'i> { } /// A raw list of CSS tokens, with embedded parsed values. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct TokenList<'i>(#[cfg_attr(feature = "serde", serde(borrow))] pub Vec>); /// A raw CSS token, or a parsed value. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -387,7 +388,7 @@ impl<'i> TokenList<'i> { /// A raw CSS token. // Copied from cssparser to change CowRcStr to CowArcStr -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -752,13 +753,15 @@ impl<'i> TokenList<'i> { } /// A CSS variable reference. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] +#[visit(visit_variable, VARIABLES)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Variable<'i> { /// The variable name. #[cfg_attr(feature = "serde", serde(borrow))] pub name: DashedIdentReference<'i>, /// A fallback value in case the variable is not defined. + #[skip_type] pub fallback: Option>, } @@ -797,7 +800,7 @@ impl<'i> Variable<'i> { /// These can be converted from the modern slash syntax to older comma syntax. /// This can only be done when the only unresolved component is the alpha /// since variables can resolve to multiple tokens. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -814,6 +817,7 @@ pub enum UnresolvedColor<'i> { b: f32, /// The unresolved alpha component. #[cfg_attr(feature = "serde", serde(borrow))] + #[skip_type] alpha: TokenList<'i>, }, /// An hsl() color. @@ -826,6 +830,7 @@ pub enum UnresolvedColor<'i> { l: f32, /// The unresolved alpha component. #[cfg_attr(feature = "serde", serde(borrow))] + #[skip_type] alpha: TokenList<'i>, }, } diff --git a/src/properties/display.rs b/src/properties/display.rs index 86ba12ec..40039d66 100644 --- a/src/properties/display.rs +++ b/src/properties/display.rs @@ -11,6 +11,7 @@ use crate::printer::Printer; use crate::targets::Browsers; use crate::traits::{Parse, PropertyHandler, ToCss}; use crate::vendor_prefix::VendorPrefix; +use crate::visitor::Visit; use cssparser::*; enum_property! { @@ -24,7 +25,7 @@ enum_property! { } /// A [``](https://drafts.csswg.org/css-display-3/#typedef-display-inside) value. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -105,7 +106,7 @@ impl DisplayInside { /// A pair of inside and outside display values, as used in the `display` property. /// /// See [Display](Display). -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct DisplayPair { /// The outside display value. @@ -318,7 +319,7 @@ enum_property! { } /// A value for the [display](https://drafts.csswg.org/css-display-3/#the-display-properties) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), diff --git a/src/properties/effects.rs b/src/properties/effects.rs index eab5fe81..e867f9a4 100644 --- a/src/properties/effects.rs +++ b/src/properties/effects.rs @@ -6,11 +6,12 @@ use crate::targets::Browsers; use crate::traits::{FallbackValues, Parse, ToCss, Zero}; use crate::values::color::ColorFallbackKind; use crate::values::{angle::Angle, color::CssColor, length::Length, percentage::NumberOrPercentage, url::Url}; +use crate::visitor::Visit; use cssparser::*; use smallvec::SmallVec; /// A [filter](https://drafts.fxtf.org/filter-effects-1/#filter-functions) function. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -205,7 +206,7 @@ impl<'i> Filter<'i> { } /// A [`drop-shadow()`](https://drafts.fxtf.org/filter-effects-1/#funcdef-filter-drop-shadow) filter function. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct DropShadow { /// The color of the drop shadow. @@ -292,7 +293,7 @@ impl DropShadow { /// A value for the [filter](https://drafts.fxtf.org/filter-effects-1/#FilterProperty) and /// [backdrop-filter](https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty) properties. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), diff --git a/src/properties/flex.rs b/src/properties/flex.rs index 6fe20bcf..ec20b909 100644 --- a/src/properties/flex.rs +++ b/src/properties/flex.rs @@ -18,6 +18,7 @@ use crate::values::{ percentage::Percentage, }; use crate::vendor_prefix::VendorPrefix; +use crate::visitor::Visit; use cssparser::*; enum_property! { diff --git a/src/properties/font.rs b/src/properties/font.rs index badaf6e5..bfca1ab9 100644 --- a/src/properties/font.rs +++ b/src/properties/font.rs @@ -13,10 +13,11 @@ use crate::traits::{Parse, PropertyHandler, Shorthand, ToCss}; use crate::values::number::CSSNumber; use crate::values::string::CowArcStr; use crate::values::{angle::Angle, length::LengthPercentage, percentage::Percentage}; +use crate::visitor::Visit; use cssparser::*; /// A value for the [font-weight](https://www.w3.org/TR/css-fonts-4/#font-weight-prop) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -73,7 +74,7 @@ impl ToCss for FontWeight { /// as used in the `font-weight` property. /// /// See [FontWeight](FontWeight). -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -156,7 +157,7 @@ enum_property! { } /// A value for the [font-size](https://www.w3.org/TR/css-fonts-4/#font-size-prop) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -252,7 +253,7 @@ impl Into for &FontStretchKeyword { } /// A value for the [font-stretch](https://www.w3.org/TR/css-fonts-4/#font-stretch-prop) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -348,7 +349,7 @@ enum_property! { } /// A value for the [font-family](https://www.w3.org/TR/css-fonts-4/#font-family-prop) property. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -429,7 +430,7 @@ impl<'i> ToCss for FontFamily<'i> { } /// A value for the [font-style](https://www.w3.org/TR/css-fonts-4/#font-style-prop) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -529,7 +530,7 @@ impl FontVariantCaps { } /// A value for the [line-height](https://www.w3.org/TR/2020/WD-css-inline-3-20200827/#propdef-line-height) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -601,7 +602,7 @@ enum_property! { /// A value for the [vertical align](https://drafts.csswg.org/css2/#propdef-vertical-align) property. // TODO: there is a more extensive spec in CSS3 but it doesn't seem any browser implements it? https://www.w3.org/TR/css-inline-3/#transverse-alignment -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), diff --git a/src/properties/grid.rs b/src/properties/grid.rs index 8c8a6607..7c7b91ba 100644 --- a/src/properties/grid.rs +++ b/src/properties/grid.rs @@ -13,13 +13,14 @@ use crate::values::ident::CustomIdent; use crate::values::length::serialize_dimension; use crate::values::number::{CSSInteger, CSSNumber}; use crate::values::{ident::CustomIdentList, length::LengthPercentage}; +use crate::visitor::Visit; use bitflags::bitflags; use cssparser::*; use smallvec::SmallVec; /// A [track sizing](https://drafts.csswg.org/css-grid-2/#track-sizing) value /// for the `grid-template-rows` and `grid-template-columns` properties. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -37,7 +38,7 @@ pub enum TrackSizing<'i> { /// as used in the `grid-template-rows` and `grid-template-columns` properties. /// /// See [TrackSizing](TrackSizing). -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct TrackList<'i> { /// A list of line names. @@ -50,7 +51,7 @@ pub struct TrackList<'i> { /// Either a track size or `repeat()` function. /// /// See [TrackList](TrackList). -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -68,7 +69,7 @@ pub enum TrackListItem<'i> { /// as used in the `grid-template-rows` and `grid-template-columns` properties. /// /// See [TrackListItem](TrackListItem). -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -91,14 +92,14 @@ impl Default for TrackSize { /// A [track size list](https://drafts.csswg.org/css-grid-2/#auto-tracks), as used /// in the `grid-auto-rows` and `grid-auto-columns` properties. -#[derive(Debug, Clone, PartialEq, Default)] +#[derive(Debug, Clone, PartialEq, Default, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct TrackSizeList(pub SmallVec<[TrackSize; 1]>); /// A [``](https://drafts.csswg.org/css-grid-2/#typedef-track-breadth) value. /// /// See [TrackSize](TrackSize). -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -121,7 +122,7 @@ pub enum TrackBreadth { /// representing the `repeat()` function in a track list. /// /// See [TrackListItem](TrackListItem). -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct TrackRepeat<'i> { /// The repeat count. @@ -137,7 +138,7 @@ pub struct TrackRepeat<'i> { /// used in the `repeat()` function. /// /// See [TrackRepeat](TrackRepeat). -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -537,7 +538,7 @@ impl ToCss for TrackSizeList { } /// A value for the [grid-template-areas](https://drafts.csswg.org/css-grid-2/#grid-template-areas-property) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -706,7 +707,7 @@ impl GridTemplateAreas { /// A value for the [grid-template](https://drafts.csswg.org/css-grid-2/#explicit-grid-shorthand) shorthand property. /// /// If `areas` is not `None`, then `rows` must also not be `None`. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct GridTemplate<'i> { /// The grid template rows. @@ -935,6 +936,7 @@ bitflags! { /// /// The `Row` or `Column` flags may be combined with the `Dense` flag, but the `Row` and `Column` flags may /// not be combined. + #[derive(Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct GridAutoFlow: u8 { /// The auto-placement algorithm places items by filling each row, adding new rows as necessary. @@ -1034,7 +1036,7 @@ impl ToCss for GridAutoFlow { /// A value for the [grid](https://drafts.csswg.org/css-grid-2/#grid-shorthand) shorthand property. /// /// Explicit and implicit values may not be combined. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Grid<'i> { /// Explicit grid template rows. @@ -1216,7 +1218,7 @@ impl_shorthand! { /// A [``](https://drafts.csswg.org/css-grid-2/#typedef-grid-row-start-grid-line) value, /// used in the `grid-row-start`, `grid-row-end`, `grid-column-start`, and `grid-column-end` properties. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), diff --git a/src/properties/list.rs b/src/properties/list.rs index 6e8a71f3..b846ac52 100644 --- a/src/properties/list.rs +++ b/src/properties/list.rs @@ -10,10 +10,11 @@ use crate::targets::Browsers; use crate::traits::{FallbackValues, Parse, PropertyHandler, Shorthand, ToCss}; use crate::values::string::CowArcStr; use crate::values::{ident::CustomIdent, image::Image}; +use crate::visitor::Visit; use cssparser::*; /// A value for the [list-style-type](https://www.w3.org/TR/2020/WD-css-lists-3-20201117/#text-markers) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -67,7 +68,7 @@ impl ToCss for ListStyleType<'_> { } /// A [counter-style](https://www.w3.org/TR/css-counter-styles-3/#typedef-counter-style) name. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -232,7 +233,7 @@ enum_property! { /// `symbols()` function. /// /// See [CounterStyle](CounterStyle). -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), diff --git a/src/properties/margin_padding.rs b/src/properties/margin_padding.rs index f20047fa..eaf9677e 100644 --- a/src/properties/margin_padding.rs +++ b/src/properties/margin_padding.rs @@ -8,6 +8,7 @@ use crate::printer::Printer; use crate::properties::{Property, PropertyId}; use crate::traits::{Parse, PropertyHandler, Shorthand, ToCss}; use crate::values::{length::LengthPercentageOrAuto, rect::Rect, size::Size2D}; +use crate::visitor::Visit; use cssparser::*; rect_shorthand! { diff --git a/src/properties/masking.rs b/src/properties/masking.rs index 6eccce3a..9583ba80 100644 --- a/src/properties/masking.rs +++ b/src/properties/masking.rs @@ -17,6 +17,7 @@ use crate::values::length::LengthOrNumber; use crate::values::rect::Rect; use crate::values::{image::Image, position::Position, shape::BasicShape, url::Url}; use crate::vendor_prefix::VendorPrefix; +use crate::visitor::Visit; use cssparser::*; use itertools::izip; use smallvec::SmallVec; @@ -102,7 +103,7 @@ impl Default for GeometryBox { } /// A value for the [mask-clip](https://www.w3.org/TR/css-masking-1/#the-mask-clip) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -372,7 +373,7 @@ impl<'i> ImageFallback<'i> for Mask<'i> { } /// A value for the [clip-path](https://www.w3.org/TR/css-masking-1/#the-clip-path) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), diff --git a/src/properties/mod.rs b/src/properties/mod.rs index 9955ddf0..90c93c14 100644 --- a/src/properties/mod.rs +++ b/src/properties/mod.rs @@ -136,6 +136,7 @@ use crate::values::{ rect::*, shape::FillRule, size::Size2D, time::Time, }; use crate::vendor_prefix::VendorPrefix; +use crate::visitor::Visit; use align::*; use animation::*; use background::*; @@ -174,7 +175,7 @@ macro_rules! define_properties { )+ ) => { /// A CSS property id. - #[derive(Debug, Clone, PartialEq)] + #[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum PropertyId<'i> { $( @@ -523,7 +524,8 @@ macro_rules! define_properties { } /// A CSS property. - #[derive(Debug, Clone, PartialEq)] + #[derive(Debug, Clone, PartialEq, Visit)] + #[visit(visit_property, PROPERTIES)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "property", content = "value"))] pub enum Property<'i> { diff --git a/src/properties/outline.rs b/src/properties/outline.rs index 3efaa5ad..a84cfd80 100644 --- a/src/properties/outline.rs +++ b/src/properties/outline.rs @@ -10,10 +10,11 @@ use crate::printer::Printer; use crate::targets::Browsers; use crate::traits::{FallbackValues, Parse, PropertyHandler, Shorthand, ToCss}; use crate::values::color::CssColor; +use crate::visitor::Visit; use cssparser::*; /// A value for the [outline-style](https://drafts.csswg.org/css-ui/#outline-style) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), diff --git a/src/properties/overflow.rs b/src/properties/overflow.rs index a0b1d7d2..052a0ca9 100644 --- a/src/properties/overflow.rs +++ b/src/properties/overflow.rs @@ -9,6 +9,7 @@ use crate::macros::{define_shorthand, enum_property}; use crate::printer::Printer; use crate::targets::Browsers; use crate::traits::{Parse, PropertyHandler, Shorthand, ToCss}; +use crate::visitor::Visit; use cssparser::*; enum_property! { diff --git a/src/properties/position.rs b/src/properties/position.rs index 73ace120..7f568f3b 100644 --- a/src/properties/position.rs +++ b/src/properties/position.rs @@ -10,10 +10,11 @@ use crate::targets::Browsers; use crate::traits::{Parse, PropertyHandler, ToCss}; use crate::values::number::CSSInteger; use crate::vendor_prefix::VendorPrefix; +use crate::visitor::Visit; use cssparser::*; /// A value for the [position](https://www.w3.org/TR/css-position-3/#position-property) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -69,7 +70,7 @@ impl ToCss for Position { } /// A value for the [z-index](https://drafts.csswg.org/css2/#z-index) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), diff --git a/src/properties/size.rs b/src/properties/size.rs index d2b6c52a..537fd2b2 100644 --- a/src/properties/size.rs +++ b/src/properties/size.rs @@ -10,6 +10,7 @@ use crate::properties::{Property, PropertyId}; use crate::traits::{Parse, PropertyHandler, ToCss}; use crate::values::length::LengthPercentage; use crate::vendor_prefix::VendorPrefix; +use crate::visitor::Visit; use cssparser::*; // https://drafts.csswg.org/css-sizing-3/#specifying-sizes @@ -17,7 +18,7 @@ use cssparser::*; /// A value for the [preferred size properties](https://drafts.csswg.org/css-sizing-3/#preferred-size-properties), /// i.e. `width` and `height. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -118,7 +119,7 @@ impl ToCss for Size { /// A value for the [minimum](https://drafts.csswg.org/css-sizing-3/#min-size-properties) /// and [maximum](https://drafts.csswg.org/css-sizing-3/#max-size-properties) size properties, /// e.g. `min-width` and `max-height`. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), diff --git a/src/properties/svg.rs b/src/properties/svg.rs index d14b98a4..395c85fc 100644 --- a/src/properties/svg.rs +++ b/src/properties/svg.rs @@ -7,11 +7,12 @@ use crate::targets::Browsers; use crate::traits::{FallbackValues, Parse, ToCss}; use crate::values::length::LengthPercentage; use crate::values::{color::CssColor, url::Url}; +use crate::visitor::Visit; use cssparser::*; /// An SVG [``](https://www.w3.org/TR/SVG2/painting.html#SpecifyingPaint) value /// used in the `fill` and `stroke` properties. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -37,7 +38,7 @@ pub enum SVGPaint<'i> { /// A fallback for an SVG paint in case a paint server `url()` cannot be resolved. /// /// See [SVGPaint](SVGPaint). -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -165,7 +166,7 @@ enum_property! { } /// A value for the [stroke-dasharray](https://www.w3.org/TR/SVG2/painting.html#StrokeDashing) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -227,7 +228,7 @@ impl ToCss for StrokeDasharray { } /// A value for the [marker](https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties) properties. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), diff --git a/src/properties/text.rs b/src/properties/text.rs index 0710d6c6..9bf709dd 100644 --- a/src/properties/text.rs +++ b/src/properties/text.rs @@ -17,6 +17,7 @@ use crate::values::color::{ColorFallbackKind, CssColor}; use crate::values::length::{Length, LengthPercentage, LengthValue}; use crate::values::string::CowArcStr; use crate::vendor_prefix::VendorPrefix; +use crate::visitor::Visit; use bitflags::bitflags; use cssparser::*; use smallvec::SmallVec; @@ -47,6 +48,7 @@ bitflags! { /// [text-transform](https://www.w3.org/TR/2021/CRD-css-text-3-20210422/#text-transform-property) property. /// /// All combinations of flags is supported. + #[derive(Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct TextTransformOther: u8 { /// Puts all typographic character units in full-width form. @@ -93,7 +95,7 @@ impl ToCss for TextTransformOther { } /// A value for the [text-transform](https://www.w3.org/TR/2021/CRD-css-text-3-20210422/#text-transform-property) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct TextTransform { /// How case should be transformed. @@ -287,7 +289,7 @@ enum_property! { /// A value for the [word-spacing](https://www.w3.org/TR/2021/CRD-css-text-3-20210422/#word-spacing-property) /// and [letter-spacing](https://www.w3.org/TR/2021/CRD-css-text-3-20210422/#letter-spacing-property) properties. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -324,7 +326,7 @@ impl ToCss for Spacing { } /// A value for the [text-indent](https://www.w3.org/TR/2021/CRD-css-text-3-20210422/#text-indent-property) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct TextIndent { /// The amount to indent. @@ -398,6 +400,7 @@ bitflags! { /// A value for the [text-decoration-line](https://www.w3.org/TR/2020/WD-css-text-decor-4-20200506/#text-decoration-line-property) property. /// /// Multiple lines may be specified by combining the flags. + #[derive(Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct TextDecorationLine: u8 { /// Each line of text is underlined. @@ -522,7 +525,7 @@ impl Default for TextDecorationStyle { } /// A value for the [text-decoration-thickness](https://www.w3.org/TR/2020/WD-css-text-decor-4-20200506/#text-decoration-width-property) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -710,7 +713,7 @@ enum_property! { } /// A value for the [text-emphasis-style](https://www.w3.org/TR/2020/WD-css-text-decor-4-20200506/#text-emphasis-style-property) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -885,7 +888,7 @@ enum_property! { } /// A value for the [text-emphasis-position](https://www.w3.org/TR/2020/WD-css-text-decor-4-20200506/#text-emphasis-position-property) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct TextEmphasisPosition { /// The vertical position. @@ -1234,7 +1237,7 @@ impl<'i> PropertyHandler<'i> for TextDecorationHandler<'i> { } /// A value for the [text-shadow](https://www.w3.org/TR/2020/WD-css-text-decor-4-20200506/#text-shadow-property) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct TextShadow { /// The color of the text shadow. diff --git a/src/properties/transform.rs b/src/properties/transform.rs index 258fd484..a62f1232 100644 --- a/src/properties/transform.rs +++ b/src/properties/transform.rs @@ -16,11 +16,12 @@ use crate::values::{ percentage::NumberOrPercentage, }; use crate::vendor_prefix::VendorPrefix; +use crate::visitor::Visit; use cssparser::*; use std::f32::consts::PI; /// A value for the [transform](https://www.w3.org/TR/2019/CR-css-transforms-1-20190214/#propdef-transform) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct TransformList(pub Vec); @@ -142,7 +143,7 @@ impl TransformList { } /// An individual [transform function](https://www.w3.org/TR/2019/CR-css-transforms-1-20190214/#two-d-transform-functions). -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -194,7 +195,7 @@ pub enum Transform { } /// A 2D matrix. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[allow(missing_docs)] pub struct Matrix { @@ -231,7 +232,7 @@ impl Matrix { } /// A 3D matrix. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[allow(missing_docs)] pub struct Matrix3d { @@ -1385,7 +1386,7 @@ enum_property! { } /// A value for the [perspective](https://drafts.csswg.org/css-transforms-2/#perspective-property) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -1421,7 +1422,7 @@ impl ToCss for Perspective { } /// A value for the [translate](https://drafts.csswg.org/css-transforms-2/#propdef-translate) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Translate { /// The x translation. @@ -1484,7 +1485,7 @@ impl Translate { } /// A value for the [rotate](https://drafts.csswg.org/css-transforms-2/#propdef-rotate) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Rotate { /// Rotation around the x axis. @@ -1568,7 +1569,7 @@ impl Rotate { } /// A value for the [scale](https://drafts.csswg.org/css-transforms-2/#propdef-scale) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Scale { /// Scale on the x axis. diff --git a/src/properties/transition.rs b/src/properties/transition.rs index f7bbd251..2ca92d5f 100644 --- a/src/properties/transition.rs +++ b/src/properties/transition.rs @@ -13,6 +13,7 @@ use crate::targets::Browsers; use crate::traits::{Parse, PropertyHandler, Shorthand, ToCss, Zero}; use crate::values::{easing::EasingFunction, time::Time}; use crate::vendor_prefix::VendorPrefix; +use crate::visitor::Visit; use cssparser::*; use itertools::izip; use smallvec::SmallVec; diff --git a/src/properties/ui.rs b/src/properties/ui.rs index cb676530..e122cd3d 100644 --- a/src/properties/ui.rs +++ b/src/properties/ui.rs @@ -11,6 +11,7 @@ use crate::values::color::CssColor; use crate::values::number::CSSNumber; use crate::values::string::CowArcStr; use crate::values::url::Url; +use crate::visitor::Visit; use cssparser::*; use smallvec::SmallVec; @@ -35,7 +36,7 @@ enum_property! { /// A [cursor image](https://www.w3.org/TR/2021/WD-css-ui-4-20210316/#cursor) value, used in the `cursor` property. /// /// See [Cursor](Cursor). -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct CursorImage<'i> { /// A url to the cursor image. @@ -123,7 +124,7 @@ enum_property! { } /// A value for the [cursor](https://www.w3.org/TR/2021/WD-css-ui-4-20210316/#cursor) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Cursor<'i> { /// A list of cursor images. @@ -165,7 +166,7 @@ impl<'i> ToCss for Cursor<'i> { } /// A value for the [caret-color](https://www.w3.org/TR/2021/WD-css-ui-4-20210316/#caret-color) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -281,7 +282,7 @@ enum_property! { } /// A value for the [appearance](https://www.w3.org/TR/2021/WD-css-ui-4-20210316/#appearance-switching) property. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), diff --git a/src/rules/container.rs b/src/rules/container.rs index ff4627ec..59c92149 100644 --- a/src/rules/container.rs +++ b/src/rules/container.rs @@ -8,26 +8,28 @@ use crate::error::{MinifyError, ParserError, PrinterError}; use crate::media_query::MediaCondition; use crate::printer::Printer; use crate::rules::{StyleContext, ToCssWithContext}; -use crate::traits::{Parse, ToCss, VisitChildren, Visitor}; +use crate::traits::{Parse, ToCss}; use crate::values::ident::CustomIdent; +use crate::visitor::Visit; /// A [@container](https://drafts.csswg.org/css-contain-3/#container-rule) rule. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct ContainerRule<'i, T> { +pub struct ContainerRule<'i, R> { /// The name of the container. #[cfg_attr(feature = "serde", serde(borrow))] pub name: Option>, /// The container condition. pub condition: MediaCondition<'i>, /// The rules within the `@container` rule. - pub rules: CssRuleList<'i, T>, + pub rules: CssRuleList<'i, R>, /// The location of the rule in the source file. + #[skip_visit] pub loc: Location, } /// A [``](https://drafts.csswg.org/css-contain-3/#typedef-container-name) in a `@container` rule. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ContainerName<'i>(#[cfg_attr(feature = "serde", serde(borrow))] pub CustomIdent<'i>); @@ -50,12 +52,6 @@ impl<'i> ToCss for ContainerName<'i> { } } -impl<'i, T: VisitChildren<'i, T, V>, V: Visitor<'i, T>> VisitChildren<'i, T, V> for ContainerRule<'i, T> { - fn visit_children(&mut self, visitor: &mut V) { - self.rules.visit_children(visitor) - } -} - impl<'i, T> ContainerRule<'i, T> { pub(crate) fn minify( &mut self, diff --git a/src/rules/counter_style.rs b/src/rules/counter_style.rs index d5530d51..c253be65 100644 --- a/src/rules/counter_style.rs +++ b/src/rules/counter_style.rs @@ -6,9 +6,10 @@ use crate::error::PrinterError; use crate::printer::Printer; use crate::traits::ToCss; use crate::values::ident::CustomIdent; +use crate::visitor::Visit; /// A [@counter-style](https://drafts.csswg.org/css-counter-styles/#the-counter-style-rule) rule. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct CounterStyleRule<'i> { /// The name of the counter style to declare. @@ -18,6 +19,7 @@ pub struct CounterStyleRule<'i> { /// Declarations in the `@counter-style` rule. pub declarations: DeclarationBlock<'i>, /// The location of the rule in the source file. + #[skip_visit] pub loc: Location, } diff --git a/src/rules/custom_media.rs b/src/rules/custom_media.rs index 2a4b377e..e4a0b73f 100644 --- a/src/rules/custom_media.rs +++ b/src/rules/custom_media.rs @@ -6,9 +6,10 @@ use crate::media_query::MediaList; use crate::printer::Printer; use crate::traits::ToCss; use crate::values::ident::DashedIdent; +use crate::visitor::Visit; /// A [@custom-media](https://drafts.csswg.org/mediaqueries-5/#custom-mq) rule. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct CustomMediaRule<'i> { /// The name of the declared media query. @@ -17,6 +18,7 @@ pub struct CustomMediaRule<'i> { /// The media query to declare. pub query: MediaList<'i>, /// The location of the rule in the source file. + #[skip_visit] pub loc: Location, } diff --git a/src/rules/document.rs b/src/rules/document.rs index 52216448..f0f73469 100644 --- a/src/rules/document.rs +++ b/src/rules/document.rs @@ -4,19 +4,21 @@ use super::Location; use super::{CssRuleList, MinifyContext}; use crate::error::{MinifyError, PrinterError}; use crate::printer::Printer; -use crate::traits::{ToCss, Visitor, VisitChildren}; +use crate::traits::ToCss; +use crate::visitor::Visit; /// A [@-moz-document](https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#at-document) rule. /// /// Note that only the `url-prefix()` function with no arguments is supported, and only the `-moz` prefix /// is allowed since Firefox was the only browser that ever implemented this rule. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct MozDocumentRule<'i, T> { +pub struct MozDocumentRule<'i, R> { /// Nested rules within the `@-moz-document` rule. #[cfg_attr(feature = "serde", serde(borrow))] - pub rules: CssRuleList<'i, T>, + pub rules: CssRuleList<'i, R>, /// The location of the rule in the source file. + #[skip_visit] pub loc: Location, } @@ -26,12 +28,6 @@ impl<'i, T> MozDocumentRule<'i, T> { } } -impl<'i, T: VisitChildren<'i, T, V>, V: Visitor<'i, T>> VisitChildren<'i, T, V> for MozDocumentRule<'i, T> { - fn visit_children(&mut self, visitor: &mut V) { - self.rules.visit_children(visitor) - } -} - impl<'i, T: ToCss> ToCss for MozDocumentRule<'i, T> { fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> where diff --git a/src/rules/font_face.rs b/src/rules/font_face.rs index 03c674ae..14cdfd9b 100644 --- a/src/rules/font_face.rs +++ b/src/rules/font_face.rs @@ -11,24 +11,26 @@ use crate::traits::{Parse, ToCss}; use crate::values::size::Size2D; use crate::values::string::CowArcStr; use crate::values::url::Url; +use crate::visitor::Visit; use cssparser::*; use std::fmt::Write; /// A [@font-face](https://drafts.csswg.org/css-fonts/#font-face-rule) rule. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct FontFaceRule<'i> { /// Declarations in the `@font-face` rule. #[cfg_attr(feature = "serde", serde(borrow))] pub properties: Vec>, /// The location of the rule in the source file. + #[skip_visit] pub loc: Location, } /// A property within an `@font-face` rule. /// /// See [FontFaceRule](FontFaceRule). -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -54,7 +56,7 @@ pub enum FontFaceProperty<'i> { /// A value for the [src](https://drafts.csswg.org/css-fonts/#src-desc) /// property in an `@font-face` rule. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -105,7 +107,7 @@ impl<'i> ToCss for Source<'i> { /// A `url()` value for the [src](https://drafts.csswg.org/css-fonts/#src-desc) /// property in an `@font-face` rule. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct UrlSource<'i> { /// The URL. @@ -163,7 +165,7 @@ impl<'i> ToCss for UrlSource<'i> { /// A font format keyword in the `format()` function of the the /// [src](https://drafts.csswg.org/css-fonts/#src-desc) /// property of an `@font-face` rule. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -276,7 +278,7 @@ enum_property! { /// A contiguous range of Unicode code points. /// /// Cannot be empty. Can represent a single code point when start == end. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct UnicodeRange { /// Inclusive start of the range. In [0, end]. diff --git a/src/rules/font_palette_values.rs b/src/rules/font_palette_values.rs index a6495cbf..800c002f 100644 --- a/src/rules/font_palette_values.rs +++ b/src/rules/font_palette_values.rs @@ -12,10 +12,11 @@ use crate::traits::{Parse, ToCss}; use crate::values::color::{ColorFallbackKind, CssColor}; use crate::values::ident::DashedIdent; use crate::values::number::CSSInteger; +use crate::visitor::Visit; use cssparser::*; /// A [@font-palette-values](https://drafts.csswg.org/css-fonts-4/#font-palette-values) rule. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct FontPaletteValuesRule<'i> { /// The name of the font palette. @@ -24,13 +25,14 @@ pub struct FontPaletteValuesRule<'i> { #[cfg_attr(feature = "serde", serde(borrow))] pub properties: Vec>, /// The location of the rule in the source file. + #[skip_visit] pub loc: Location, } /// A property within an `@font-palette-values` rule. /// /// See [FontPaletteValuesRule](FontPaletteValuesRule). -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -50,7 +52,7 @@ pub enum FontPaletteValuesProperty<'i> { /// A value for the [base-palette](https://drafts.csswg.org/css-fonts-4/#base-palette-desc) /// property in an `@font-palette-values` rule. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -67,7 +69,7 @@ pub enum BasePalette { /// A value for the [override-colors](https://drafts.csswg.org/css-fonts-4/#override-color) /// property in an `@font-palette-values` rule. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct OverrideColors { /// The index of the color within the palette to override. diff --git a/src/rules/import.rs b/src/rules/import.rs index 9ac009f2..eaf3f8b6 100644 --- a/src/rules/import.rs +++ b/src/rules/import.rs @@ -9,22 +9,26 @@ use crate::media_query::MediaList; use crate::printer::Printer; use crate::traits::ToCss; use crate::values::string::CowArcStr; +use crate::visitor::Visit; use cssparser::*; /// A [@import](https://drafts.csswg.org/css-cascade/#at-import) rule. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ImportRule<'i> { /// The url to import. #[cfg_attr(feature = "serde", serde(borrow))] + #[skip_visit] pub url: CowArcStr<'i>, /// An optional cascade layer name, or `None` for an anonymous layer. + #[skip_visit] pub layer: Option>>, /// An optional `supports()` condition. pub supports: Option>, /// A media query. pub media: MediaList<'i>, /// The location of the rule in the source file. + #[skip_visit] pub loc: Location, } diff --git a/src/rules/keyframes.rs b/src/rules/keyframes.rs index 7f593b70..fd49bd44 100644 --- a/src/rules/keyframes.rs +++ b/src/rules/keyframes.rs @@ -17,10 +17,11 @@ use crate::values::ident::CustomIdent; use crate::values::percentage::Percentage; use crate::values::string::CowArcStr; use crate::vendor_prefix::VendorPrefix; +use crate::visitor::Visit; use cssparser::*; /// A [@keyframes](https://drafts.csswg.org/css-animations/#keyframes) rule. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct KeyframesRule<'i> { /// The animation name. @@ -30,13 +31,15 @@ pub struct KeyframesRule<'i> { /// A list of keyframes in the animation. pub keyframes: Vec>, /// A vendor prefix for the rule, e.g. `@-webkit-keyframes`. + #[skip_visit] pub vendor_prefix: VendorPrefix, /// The location of the rule in the source file. + #[skip_visit] pub loc: Location, } /// KeyframesName -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum KeyframesName<'i> { /// `` of a `@keyframes` name. @@ -245,7 +248,7 @@ impl<'i> ToCss for KeyframesRule<'i> { /// A [keyframe selector](https://drafts.csswg.org/css-animations/#typedef-keyframe-selector) /// within an `@keyframes` rule. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -306,7 +309,7 @@ impl ToCss for KeyframeSelector { /// An individual keyframe within an `@keyframes` rule. /// /// See [KeyframesRule](KeyframesRule). -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Keyframe<'i> { /// A list of keyframe selectors to associate with the declarations in this keyframe. diff --git a/src/rules/layer.rs b/src/rules/layer.rs index 82d61a2f..97ba32d0 100644 --- a/src/rules/layer.rs +++ b/src/rules/layer.rs @@ -3,8 +3,9 @@ use super::{CssRuleList, Location, MinifyContext}; use crate::error::{MinifyError, ParserError, PrinterError}; use crate::printer::Printer; -use crate::traits::{Parse, ToCss, Visitor, VisitChildren}; +use crate::traits::{Parse, ToCss}; use crate::values::string::CowArcStr; +use crate::visitor::Visit; use cssparser::*; use smallvec::SmallVec; @@ -78,13 +79,15 @@ impl<'i> ToCss for LayerName<'i> { /// A [@layer statement](https://drafts.csswg.org/css-cascade-5/#layer-empty) rule. /// /// See also [LayerBlockRule](LayerBlockRule). -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct LayerStatementRule<'i> { /// The layer names to declare. #[cfg_attr(feature = "serde", serde(borrow))] + #[skip_visit] pub names: Vec>, /// The location of the rule in the source file. + #[skip_visit] pub loc: Location, } @@ -101,15 +104,17 @@ impl<'i> ToCss for LayerStatementRule<'i> { } /// A [@layer block](https://drafts.csswg.org/css-cascade-5/#layer-block) rule. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct LayerBlockRule<'i, T> { +pub struct LayerBlockRule<'i, R> { /// The name of the layer to declare, or `None` to declare an anonymous layer. #[cfg_attr(feature = "serde", serde(borrow))] + #[skip_visit] pub name: Option>, /// The rules within the `@layer` rule. - pub rules: CssRuleList<'i, T>, + pub rules: CssRuleList<'i, R>, /// The location of the rule in the source file. + #[skip_visit] pub loc: Location, } @@ -125,12 +130,6 @@ impl<'i, T> LayerBlockRule<'i, T> { } } -impl<'i, T: VisitChildren<'i, T, V>, V: Visitor<'i, T>> VisitChildren<'i, T, V> for LayerBlockRule<'i, T> { - fn visit_children(&mut self, visitor: &mut V) { - self.rules.visit_children(visitor) - } -} - impl<'i, T: ToCss> ToCss for LayerBlockRule<'i, T> { fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> where diff --git a/src/rules/media.rs b/src/rules/media.rs index 7691b051..7d4f6f0a 100644 --- a/src/rules/media.rs +++ b/src/rules/media.rs @@ -6,18 +6,20 @@ use crate::error::{MinifyError, PrinterError}; use crate::media_query::MediaList; use crate::printer::Printer; use crate::rules::{StyleContext, ToCssWithContext}; -use crate::traits::{ToCss, Visitor, VisitChildren}; +use crate::traits::ToCss; +use crate::visitor::Visit; /// A [@media](https://drafts.csswg.org/css-conditional-3/#at-media) rule. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct MediaRule<'i, T> { +pub struct MediaRule<'i, R> { /// The media query list. #[cfg_attr(feature = "serde", serde(borrow))] pub query: MediaList<'i>, /// The rules within the `@media` rule. - pub rules: CssRuleList<'i, T>, + pub rules: CssRuleList<'i, R>, /// The location of the rule in the source file. + #[skip_visit] pub loc: Location, } @@ -37,12 +39,6 @@ impl<'i, T> MediaRule<'i, T> { } } -impl<'i, T: VisitChildren<'i, T, V>, V: Visitor<'i, T>> VisitChildren<'i, T, V> for MediaRule<'i, T> { - fn visit_children(&mut self, visitor: &mut V) { - self.rules.visit_children(visitor) - } -} - impl<'a, 'i, T: ToCss> ToCssWithContext<'a, 'i, T> for MediaRule<'i, T> { fn to_css_with_context( &self, diff --git a/src/rules/mod.rs b/src/rules/mod.rs index 6fdb2b35..0bb22b3a 100644 --- a/src/rules/mod.rs +++ b/src/rules/mod.rs @@ -69,12 +69,13 @@ use crate::rules::keyframes::KeyframesName; use crate::selector::{downlevel_selectors, get_prefix, is_equivalent}; use crate::stylesheet::ParserOptions; use crate::targets::Browsers; -use crate::traits::{ToCss, VisitChildren, Visitor}; +use crate::traits::ToCss; use crate::values::string::CowArcStr; use crate::vendor_prefix::VendorPrefix; +use crate::visitor::{Visit, VisitTypes, Visitor}; use container::ContainerRule; use counter_style::CounterStyleRule; -use cssparser::{parse_one_rule, ParseError, Parser, ParserInput, AtRuleParser}; +use cssparser::{parse_one_rule, AtRuleParser, ParseError, Parser, ParserInput}; use custom_media::CustomMediaRule; use document::MozDocumentRule; use font_face::FontFaceRule; @@ -120,20 +121,21 @@ pub struct Location { } /// A CSS rule. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Visit)] +#[visit(visit_rule, RULES)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(tag = "type", content = "value", rename_all = "kebab-case") )] -pub enum CssRule<'i, T> { +pub enum CssRule<'i, R> { /// A `@media` rule. #[cfg_attr(feature = "serde", serde(borrow))] - Media(MediaRule<'i, T>), + Media(MediaRule<'i, R>), /// An `@import` rule. Import(ImportRule<'i>), /// A style rule. - Style(StyleRule<'i, T>), + Style(StyleRule<'i, R>), /// A `@keyframes` rule. Keyframes(KeyframesRule<'i>), /// A `@font-face` rule. @@ -143,15 +145,15 @@ pub enum CssRule<'i, T> { /// A `@page` rule. Page(PageRule<'i>), /// A `@supports` rule. - Supports(SupportsRule<'i, T>), + Supports(SupportsRule<'i, R>), /// A `@counter-style` rule. CounterStyle(CounterStyleRule<'i>), /// A `@namespace` rule. Namespace(NamespaceRule<'i>), /// A `@-moz-document` rule. - MozDocument(MozDocumentRule<'i, T>), + MozDocument(MozDocumentRule<'i, R>), /// A `@nest` rule. - Nesting(NestingRule<'i, T>), + Nesting(NestingRule<'i, R>), /// A `@viewport` rule. Viewport(ViewportRule<'i>), /// A `@custom-media` rule. @@ -159,17 +161,17 @@ pub enum CssRule<'i, T> { /// A `@layer` statement rule. LayerStatement(LayerStatementRule<'i>), /// A `@layer` block rule. - LayerBlock(LayerBlockRule<'i, T>), + LayerBlock(LayerBlockRule<'i, R>), /// A `@property` rule. Property(PropertyRule<'i>), /// A `@container` rule. - Container(ContainerRule<'i, T>), + Container(ContainerRule<'i, R>), /// A placeholder for a rule that was removed. Ignored, /// An unknown at-rule. Unknown(UnknownAtRule<'i>), /// A custom at-rule. - Custom(T) + Custom(R), } impl<'a, 'i, T: ToCss> ToCssWithContext<'a, 'i, T> for CssRule<'i, T> { @@ -201,7 +203,10 @@ impl<'a, 'i, T: ToCss> ToCssWithContext<'a, 'i, T> for CssRule<'i, T> { CssRule::Property(property) => property.to_css(dest), CssRule::Container(container) => container.to_css_with_context(dest, context), CssRule::Unknown(unknown) => unknown.to_css(dest), - CssRule::Custom(rule) => rule.to_css(dest).map_err(|_| PrinterError { kind: PrinterErrorKind::FmtError, loc: None }), + CssRule::Custom(rule) => rule.to_css(dest).map_err(|_| PrinterError { + kind: PrinterErrorKind::FmtError, + loc: None, + }), CssRule::Ignored => Ok(()), } } @@ -240,7 +245,16 @@ impl<'i, T: ToCss> ToCss for CssRule<'i, T> { /// A list of CSS rules. #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct CssRuleList<'i, T>(#[cfg_attr(feature = "serde", serde(borrow))] pub Vec>); +pub struct CssRuleList<'i, R>(#[cfg_attr(feature = "serde", serde(borrow))] pub Vec>); + +// Manually implemented to avoid circular child types. +impl<'i, T: Visit<'i, T, V>, V: Visitor<'i, T>> Visit<'i, T, V> for CssRuleList<'i, T> { + const CHILD_TYPES: VisitTypes = VisitTypes::all(); + + fn visit_children(&mut self, visitor: &mut V) { + self.0.visit(visitor) + } +} pub(crate) struct MinifyContext<'a, 'i> { pub targets: &'a Option, @@ -252,30 +266,6 @@ pub(crate) struct MinifyContext<'a, 'i> { pub css_modules: bool, } -impl<'i, T: VisitChildren<'i, T, V>, V: Visitor<'i, T>> VisitChildren<'i, T, V> for CssRuleList<'i, T> { - fn visit_children(&mut self, visitor: &mut V) { - for rule in self.0.iter_mut() { - visitor.visit_rule(rule); - } - } -} - -impl<'i, T: VisitChildren<'i, T, V>, V: Visitor<'i, T>> VisitChildren<'i, T, V> for CssRule<'i, T> { - fn visit_children(&mut self, visitor: &mut V) { - match self { - CssRule::Media(media) => media.visit_children(visitor), - CssRule::Style(style) => style.visit_children(visitor), - CssRule::Supports(supports) => supports.visit_children(visitor), - CssRule::MozDocument(document) => document.visit_children(visitor), - CssRule::Nesting(nesting) => nesting.visit_children(visitor), - CssRule::LayerBlock(layer) => layer.visit_children(visitor), - CssRule::Container(container) => container.visit_children(visitor), - CssRule::Custom(rule) => rule.visit_children(visitor), - _ => {} - } - } -} - impl<'i, T> CssRuleList<'i, T> { pub(crate) fn minify( &mut self, diff --git a/src/rules/namespace.rs b/src/rules/namespace.rs index 8ace837e..bbca0e4a 100644 --- a/src/rules/namespace.rs +++ b/src/rules/namespace.rs @@ -5,19 +5,23 @@ use crate::error::PrinterError; use crate::printer::Printer; use crate::traits::ToCss; use crate::values::string::CowArcStr; +use crate::visitor::Visit; use cssparser::*; /// A [@namespace](https://drafts.csswg.org/css-namespaces/#declaration) rule. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct NamespaceRule<'i> { /// An optional namespace prefix to declare, or `None` to declare the default namespace. #[cfg_attr(feature = "serde", serde(borrow))] + #[skip_visit] pub prefix: Option>, /// The url of the namespace. #[cfg_attr(feature = "serde", serde(borrow))] + #[skip_visit] pub url: CowArcStr<'i>, /// The location of the rule in the source file. + #[skip_visit] pub loc: Location, } diff --git a/src/rules/nesting.rs b/src/rules/nesting.rs index f9d2014b..f1a46070 100644 --- a/src/rules/nesting.rs +++ b/src/rules/nesting.rs @@ -7,17 +7,16 @@ use crate::error::{MinifyError, PrinterError}; use crate::printer::Printer; use crate::rules::{StyleContext, ToCssWithContext}; use crate::traits::ToCss; -use crate::traits::VisitChildren; -use crate::traits::Visitor; - +use crate::visitor::Visit; /// A [@nest](https://www.w3.org/TR/css-nesting-1/#at-nest) rule. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct NestingRule<'i, T> { +pub struct NestingRule<'i, R> { /// The style rule that defines the selector and declarations for the `@nest` rule. #[cfg_attr(feature = "serde", serde(borrow))] - pub style: StyleRule<'i, T>, + pub style: StyleRule<'i, R>, /// The location of the rule in the source file. + #[skip_visit] pub loc: Location, } @@ -31,12 +30,6 @@ impl<'i, T> NestingRule<'i, T> { } } -impl<'i, T: VisitChildren<'i, T, V>, V: Visitor<'i, T>> VisitChildren<'i, T, V> for NestingRule<'i, T> { - fn visit_children(&mut self, visitor: &mut V) { - self.style.visit_children(visitor) - } -} - impl<'a, 'i, T: ToCss> ToCssWithContext<'a, 'i, T> for NestingRule<'i, T> { fn to_css_with_context( &self, diff --git a/src/rules/page.rs b/src/rules/page.rs index e95400df..1475e9ec 100644 --- a/src/rules/page.rs +++ b/src/rules/page.rs @@ -7,6 +7,7 @@ use crate::macros::enum_property; use crate::printer::Printer; use crate::traits::{Parse, ToCss}; use crate::values::string::CowArcStr; +use crate::visitor::Visit; use cssparser::*; /// A [page selector](https://www.w3.org/TR/css-page-3/#typedef-page-selector) @@ -69,15 +70,17 @@ impl<'i> Parse<'i> for PageSelector<'i> { } /// A [@page](https://www.w3.org/TR/css-page-3/#at-page-rule) rule. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct PageRule<'i> { /// A list of page selectors. #[cfg_attr(feature = "serde", serde(borrow))] + #[skip_visit] pub selectors: Vec>, /// The declarations within the `@page` rule. pub declarations: DeclarationBlock<'i>, /// The location of the rule in the source file. + #[skip_visit] pub loc: Location, } diff --git a/src/rules/property.rs b/src/rules/property.rs index e7342a2e..bff1dc08 100644 --- a/src/rules/property.rs +++ b/src/rules/property.rs @@ -9,23 +9,28 @@ use crate::{ ident::DashedIdent, syntax::{ParsedComponent, SyntaxString}, }, + visitor::Visit, }; use cssparser::*; /// A [@property](https://drafts.css-houdini.org/css-properties-values-api/#at-property-rule) rule. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct PropertyRule<'i> { /// The name of the custom property to declare. #[cfg_attr(feature = "serde", serde(borrow))] pub name: DashedIdent<'i>, /// A syntax string to specify the grammar for the custom property. + #[skip_visit] pub syntax: SyntaxString, /// Whether the custom property is inherited. + #[skip_visit] pub inherits: bool, /// An optional initial value for the custom property. + #[skip_visit] pub initial_value: Option>, /// The location of the rule in the source file. + #[skip_visit] pub loc: Location, } diff --git a/src/rules/style.rs b/src/rules/style.rs index 7234b379..412453ce 100644 --- a/src/rules/style.rs +++ b/src/rules/style.rs @@ -14,9 +14,8 @@ use crate::rules::{CssRuleList, StyleContext, ToCssWithContext}; use crate::selector::{is_compatible, is_unused, Selectors}; use crate::targets::Browsers; use crate::traits::ToCss; -use crate::traits::VisitChildren; -use crate::traits::Visitor; use crate::vendor_prefix::VendorPrefix; +use crate::visitor::Visit; use cssparser::*; use parcel_selectors::SelectorList; @@ -24,9 +23,9 @@ use parcel_selectors::SelectorList; use crate::selector::{deserialize_selectors, serialize_selectors}; /// A CSS [style rule](https://drafts.csswg.org/css-syntax/#style-rules). -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct StyleRule<'i, T> { +pub struct StyleRule<'i, R> { /// The selectors for the style rule. #[cfg_attr( feature = "serde", @@ -36,24 +35,21 @@ pub struct StyleRule<'i, T> { borrow ) )] + #[skip_visit] pub selectors: SelectorList<'i, Selectors>, /// A vendor prefix override, used during selector printing. #[cfg_attr(feature = "serde", serde(skip, default = "VendorPrefix::empty"))] + #[skip_visit] pub vendor_prefix: VendorPrefix, /// The declarations within the style rule. pub declarations: DeclarationBlock<'i>, /// Nested rules within the style rule. - pub rules: CssRuleList<'i, T>, + pub rules: CssRuleList<'i, R>, /// The location of the rule in the source file. + #[skip_visit] pub loc: Location, } -impl<'i, T: VisitChildren<'i, T, V>, V: Visitor<'i, T>> VisitChildren<'i, T, V> for StyleRule<'i, T> { - fn visit_children(&mut self, visitor: &mut V) { - self.rules.visit_children(visitor) - } -} - impl<'i, T> StyleRule<'i, T> { pub(crate) fn minify( &mut self, diff --git a/src/rules/supports.rs b/src/rules/supports.rs index 4f35e619..0d3f3685 100644 --- a/src/rules/supports.rs +++ b/src/rules/supports.rs @@ -5,20 +5,22 @@ use super::{CssRuleList, MinifyContext}; use crate::error::{MinifyError, ParserError, PrinterError}; use crate::printer::Printer; use crate::rules::{StyleContext, ToCssWithContext}; -use crate::traits::{Parse, ToCss, Visitor, VisitChildren}; +use crate::traits::{Parse, ToCss}; use crate::values::string::CowArcStr; +use crate::visitor::Visit; use cssparser::*; /// A [@supports](https://drafts.csswg.org/css-conditional-3/#at-supports) rule. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct SupportsRule<'i, T> { +pub struct SupportsRule<'i, R> { /// The supports condition. #[cfg_attr(feature = "serde", serde(borrow))] pub condition: SupportsCondition<'i>, /// The rules within the `@supports` rule. - pub rules: CssRuleList<'i, T>, + pub rules: CssRuleList<'i, R>, /// The location of the rule in the source file. + #[skip_visit] pub loc: Location, } @@ -32,12 +34,6 @@ impl<'i, T> SupportsRule<'i, T> { } } -impl<'i, T: VisitChildren<'i, T, V>, V: Visitor<'i, T>> VisitChildren<'i, T, V> for SupportsRule<'i, T> { - fn visit_children(&mut self, visitor: &mut V) { - self.rules.visit_children(visitor) - } -} - impl<'a, 'i, T: ToCss> ToCssWithContext<'a, 'i, T> for SupportsRule<'i, T> { fn to_css_with_context( &self, @@ -63,7 +59,8 @@ impl<'a, 'i, T: ToCss> ToCssWithContext<'a, 'i, T> for SupportsRule<'i, T> { /// A [``](https://drafts.csswg.org/css-conditional-3/#typedef-supports-condition), /// as used in the `@supports` and `@import` rules. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Visit)] +#[visit(visit_supports_condition, SUPPORTS_CONDITIONS)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -71,10 +68,13 @@ impl<'a, 'i, T: ToCss> ToCssWithContext<'a, 'i, T> for SupportsRule<'i, T> { )] pub enum SupportsCondition<'i> { /// A `not` expression. + #[skip_type] Not(Box>), /// An `and` expression. + #[skip_type] And(Vec>), /// An `or` expression. + #[skip_type] Or(Vec>), /// A declaration to evaluate. #[cfg_attr(feature = "serde", serde(borrow))] @@ -83,6 +83,7 @@ pub enum SupportsCondition<'i> { Selector(CowArcStr<'i>), // FontTechnology() /// A parenthesized expression. + #[skip_type] Parens(Box>), /// An unknown condition. Unknown(CowArcStr<'i>), diff --git a/src/rules/unknown.rs b/src/rules/unknown.rs index c36457dc..1c831c7c 100644 --- a/src/rules/unknown.rs +++ b/src/rules/unknown.rs @@ -6,19 +6,22 @@ use crate::printer::Printer; use crate::properties::custom::TokenList; use crate::traits::ToCss; use crate::values::string::CowArcStr; +use crate::visitor::Visit; /// An unknown at-rule, stored as raw tokens. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct UnknownAtRule<'i> { /// The name of the at-rule (without the @). #[cfg_attr(feature = "serde", serde(borrow))] + #[skip_visit] pub name: CowArcStr<'i>, /// The prelude of the rule. pub prelude: TokenList<'i>, /// The contents of the block, if any. pub block: Option>, /// The location of the rule in the source file. + #[skip_visit] pub loc: Location, } diff --git a/src/rules/viewport.rs b/src/rules/viewport.rs index a3277ead..ca437780 100644 --- a/src/rules/viewport.rs +++ b/src/rules/viewport.rs @@ -6,17 +6,20 @@ use crate::error::PrinterError; use crate::printer::Printer; use crate::traits::ToCss; use crate::vendor_prefix::VendorPrefix; +use crate::visitor::Visit; /// A [@viewport](https://drafts.csswg.org/css-device-adapt/#atviewport-rule) rule. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ViewportRule<'i> { /// The vendor prefix for this rule, e.g. `@-ms-viewport`. + #[skip_visit] pub vendor_prefix: VendorPrefix, /// The declarations within the `@viewport` rule. #[cfg_attr(feature = "serde", serde(borrow))] pub declarations: DeclarationBlock<'i>, /// The location of the rule in the source file. + #[skip_visit] pub loc: Location, } diff --git a/src/selector.rs b/src/selector.rs index e75a580a..f1bcee79 100644 --- a/src/selector.rs +++ b/src/selector.rs @@ -8,6 +8,7 @@ use crate::stylesheet::{ParserOptions, PrinterOptions}; use crate::targets::Browsers; use crate::traits::{Parse, ToCss}; use crate::vendor_prefix::VendorPrefix; +use crate::visitor::Visit; use crate::{macros::enum_property, values::string::CowArcStr}; use cssparser::*; use parcel_selectors::parser::SelectorParseErrorKind; @@ -87,7 +88,8 @@ impl<'i> SelectorImpl<'i> for Selectors { fn to_css(selectors: &SelectorList<'i, Self>, dest: &mut W) -> std::fmt::Result { let mut printer = Printer::new(dest, PrinterOptions::default()); - serialize_selector_list::<_, _, DefaultAtRule>(selectors.0.iter(), &mut printer, None, false).map_err(|_| std::fmt::Error) + serialize_selector_list::<_, _, DefaultAtRule>(selectors.0.iter(), &mut printer, None, false) + .map_err(|_| std::fmt::Error) } } @@ -915,8 +917,9 @@ impl<'a, 'i, T> ToCssWithContext<'a, 'i, T> for SelectorList<'i, Selectors> { impl<'i> ToCss for SelectorList<'i, Selectors> { fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> - where - W: std::fmt::Write { + where + W: std::fmt::Write, + { serialize_selector_list::<_, _, DefaultAtRule>(self.0.iter(), dest, None, false) } } diff --git a/src/traits.rs b/src/traits.rs index da2db03a..5448116e 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -5,7 +5,6 @@ use crate::declaration::{DeclarationBlock, DeclarationList}; use crate::error::{ParserError, PrinterError}; use crate::printer::Printer; use crate::properties::{Property, PropertyId}; -use crate::rules::CssRule; use crate::stylesheet::{ParserOptions, PrinterOptions}; use crate::targets::Browsers; use crate::vendor_prefix::VendorPrefix; @@ -96,18 +95,6 @@ pub(crate) mod private { } } -/// A trait for visiting or transforming rules. -pub trait Visitor<'i, T> { - /// Visits a rule. - fn visit_rule(&mut self, rule: &mut CssRule<'i, T>); -} - -/// A trait for visiting the children of a rule. -pub trait VisitChildren<'i, T, V: Visitor<'i, T>> { - /// Visit the children of this rule. - fn visit_children(&mut self, visitor: &mut V); -} - pub(crate) trait FromStandard: Sized { fn from_standard(val: &T) -> Option; } diff --git a/src/values/alpha.rs b/src/values/alpha.rs index d8454f29..77554d2f 100644 --- a/src/values/alpha.rs +++ b/src/values/alpha.rs @@ -4,13 +4,14 @@ use super::percentage::NumberOrPercentage; use crate::error::{ParserError, PrinterError}; use crate::printer::Printer; use crate::traits::{Parse, ToCss}; +use crate::visitor::Visit; use cssparser::*; /// A CSS [``](https://www.w3.org/TR/css-color-4/#typedef-alpha-value), /// used to represent opacity. /// /// Parses either a `` or ``, but is always stored and serialized as a number. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct AlphaValue(pub f32); diff --git a/src/values/angle.rs b/src/values/angle.rs index a5d1e23d..92238ed2 100644 --- a/src/values/angle.rs +++ b/src/values/angle.rs @@ -11,6 +11,7 @@ use crate::traits::{ private::{AddInternal, TryAdd}, Map, Op, Parse, Sign, ToCss, Zero, }; +use crate::visitor::Visit; use cssparser::*; use std::f32::consts::PI; @@ -18,7 +19,8 @@ use std::f32::consts::PI; /// /// Angles may be explicit or computed by `calc()`, but are always stored and serialized /// as their computed value. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Visit)] +#[visit(visit_angle, ANGLES)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), diff --git a/src/values/calc.rs b/src/values/calc.rs index 8842b080..784fdf93 100644 --- a/src/values/calc.rs +++ b/src/values/calc.rs @@ -6,6 +6,7 @@ use crate::macros::enum_property; use crate::printer::Printer; use crate::traits::private::AddInternal; use crate::traits::{Parse, Sign, ToCss, TryMap, TryOp, TrySign}; +use crate::visitor::Visit; use cssparser::*; use super::angle::Angle; @@ -18,7 +19,7 @@ use super::time::Time; /// /// Math functions may be used in most properties and values that accept numeric /// values, including lengths, percentages, angles, times, etc. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -199,7 +200,7 @@ impl + TrySign + Clone + std::fmt::Deb /// /// This type supports generic value types. Values such as [Length](super::length::Length), [Percentage](super::percentage::Percentage), /// [Time](super::time::Time), and [Angle](super::angle::Angle) support `calc()` expressions. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -211,10 +212,13 @@ pub enum Calc { /// A literal number. Number(CSSNumber), /// A sum of two calc expressions. + #[skip_type] Sum(Box>, Box>), /// A product of a number and another calc expression. + #[skip_type] Product(CSSNumber, Box>), /// A math function, such as `calc()`, `min()`, or `max()`. + #[skip_type] Function(Box>), } diff --git a/src/values/color.rs b/src/values/color.rs index 3ba7a1c2..adb322fb 100644 --- a/src/values/color.rs +++ b/src/values/color.rs @@ -13,6 +13,7 @@ use crate::printer::Printer; use crate::rules::supports::SupportsCondition; use crate::targets::Browsers; use crate::traits::{FallbackValues, Parse, ToCss}; +use crate::visitor::{Visit, VisitTypes, Visitor}; use bitflags::bitflags; use cssparser::*; use std::any::TypeId; @@ -28,7 +29,8 @@ use std::fmt::Write; /// Each color space is represented as a struct that implements the `From` and `Into` traits /// for all other color spaces, so it is possible to convert between color spaces easily. /// In addition, colors support [interpolation](#method.interpolate) as in the `color-mix()` function. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] +#[visit(visit_color, COLORS)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -48,7 +50,7 @@ pub enum CssColor { } /// A color in a LAB color space, including the `lab()`, `lch()`, `oklab()`, and `oklch()` functions. -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -66,7 +68,7 @@ pub enum LABColor { } /// A color in a predefined color space, e.g. `display-p3`. -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -102,7 +104,7 @@ pub enum PredefinedColor { /// A floating point representation of color types that /// are usually stored as RGBA. These are used when there /// are any `none` components, which are represented as NaN. -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -1161,7 +1163,7 @@ macro_rules! define_colorspace { } ) => { $(#[$outer])* - #[derive(Debug, Clone, Copy, PartialEq)] + #[derive(Debug, Clone, Copy, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct $name { $(#[$a_meta])* @@ -3115,3 +3117,8 @@ impl HueInterpolationMethod { } } } + +impl<'i, V: Visitor<'i, T>, T> Visit<'i, T, V> for RGBA { + const CHILD_TYPES: VisitTypes = VisitTypes::empty(); + fn visit_children(&mut self, _: &mut V) {} +} diff --git a/src/values/easing.rs b/src/values/easing.rs index 490495c9..54203999 100644 --- a/src/values/easing.rs +++ b/src/values/easing.rs @@ -4,11 +4,12 @@ use crate::error::{ParserError, PrinterError}; use crate::printer::Printer; use crate::traits::{Parse, ToCss}; use crate::values::number::{CSSInteger, CSSNumber}; +use crate::visitor::Visit; use cssparser::*; use std::fmt::Write; /// A CSS [easing function](https://www.w3.org/TR/css-easing-1/#easing-functions). -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -130,7 +131,7 @@ impl EasingFunction { } /// A [step position](https://www.w3.org/TR/css-easing-1/#step-position), used within the `steps()` function. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), diff --git a/src/values/gradient.rs b/src/values/gradient.rs index 14bc0796..fa61f60b 100644 --- a/src/values/gradient.rs +++ b/src/values/gradient.rs @@ -15,10 +15,11 @@ use crate::printer::Printer; use crate::targets::Browsers; use crate::traits::{Parse, ToCss, TrySign, Zero}; use crate::vendor_prefix::VendorPrefix; +use crate::visitor::Visit; use cssparser::*; /// A CSS [``](https://www.w3.org/TR/css-images-3/#gradients) value. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -198,7 +199,7 @@ impl ToCss for Gradient { } /// A CSS [`linear-gradient()`](https://www.w3.org/TR/css-images-3/#linear-gradients) or `repeating-linear-gradient()`. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct LinearGradient { /// The direction of the gradient. @@ -295,7 +296,7 @@ impl LinearGradient { } /// A CSS [`radial-gradient()`](https://www.w3.org/TR/css-images-3/#radial-gradients) or `repeating-radial-gradient()`. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct RadialGradient { /// The shape of the gradient. @@ -366,7 +367,7 @@ impl RadialGradient { /// The direction of a CSS `linear-gradient()`. /// /// See [LinearGradient](LinearGradient). -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -459,7 +460,7 @@ impl LineDirection { /// A `radial-gradient()` [ending shape](https://www.w3.org/TR/css-images-3/#valdef-radial-gradient-ending-shape). /// /// See [RadialGradient](RadialGradient). -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -508,7 +509,7 @@ impl ToCss for EndingShape { /// A circle ending shape for a `radial-gradient()`. /// /// See [RadialGradient](RadialGradient). -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -575,7 +576,7 @@ impl ToCss for Circle { /// An ellipse ending shape for a `radial-gradient()`. /// /// See [RadialGradient](RadialGradient). -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -659,7 +660,7 @@ enum_property! { } /// A CSS [`conic-gradient()`](https://www.w3.org/TR/css-images-4/#conic-gradients) or `repeating-conic-gradient()`. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ConicGradient { /// The angle of the gradient. @@ -737,7 +738,7 @@ impl ConicGradient { /// /// This type is generic, and may be either a [LengthPercentage](super::length::LengthPercentage) /// or [Angle](super::angle::Angle) depending on what type of gradient it is within. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ColorStop { /// The color of the color stop. @@ -772,7 +773,7 @@ impl ToCss for ColorStop { /// /// This type is generic, and items may be either a [LengthPercentage](super::length::LengthPercentage) /// or [Angle](super::angle::Angle) depending on what type of gradient it is within. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -916,7 +917,7 @@ where } /// A legacy `-webkit-gradient()`. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -1060,7 +1061,7 @@ impl WebKitGradient { } /// A color stop within a legacy `-webkit-gradient()`. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct WebKitColorStop { /// The color of the color stop. @@ -1121,7 +1122,7 @@ impl WebKitColorStop { } /// An x/y position within a legacy `-webkit-gradient()`. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct WebKitGradientPoint { /// The x-position. @@ -1150,7 +1151,7 @@ impl ToCss for WebKitGradientPoint { } /// A keyword or number within a [WebKitGradientPoint](WebKitGradientPoint). -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), diff --git a/src/values/ident.rs b/src/values/ident.rs index 498e7554..851061db 100644 --- a/src/values/ident.rs +++ b/src/values/ident.rs @@ -5,6 +5,7 @@ use crate::printer::Printer; use crate::properties::css_modules::Specifier; use crate::traits::{Parse, ParseWithOptions, ToCss}; use crate::values::string::CowArcStr; +use crate::visitor::Visit; use cssparser::*; use smallvec::SmallVec; @@ -13,7 +14,8 @@ use smallvec::SmallVec; /// Custom idents are author defined, and allow any valid identifier except the /// [CSS-wide keywords](https://www.w3.org/TR/css-values-4/#css-wide-keywords). /// They may be renamed to include a hash when compiled as part of a CSS module. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Visit)] +#[visit(visit_custom_ident, CUSTOM_IDENTS)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct CustomIdent<'i>(#[cfg_attr(feature = "serde", serde(borrow))] pub CowArcStr<'i>); @@ -50,7 +52,8 @@ pub type CustomIdentList<'i> = SmallVec<[CustomIdent<'i>; 1]>; /// /// Dashed idents are used in cases where an identifier can be either author defined _or_ CSS-defined. /// Author defined idents must start with two dash characters ("--") or parsing will fail. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Visit)] +#[visit(visit_dashed_ident, DASHED_IDENTS)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct DashedIdent<'i>(#[cfg_attr(feature = "serde", serde(borrow))] pub CowArcStr<'i>); @@ -82,7 +85,7 @@ impl<'i> ToCss for DashedIdent<'i> { /// /// In CSS modules, when the `dashed_idents` option is enabled, the identifier may be followed by the /// `from` keyword and an argument indicating where the referenced identifier is declared (e.g. a filename). -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct DashedIdentReference<'i> { /// The referenced identifier. diff --git a/src/values/image.rs b/src/values/image.rs index 5d5ec8d3..67df7ca9 100644 --- a/src/values/image.rs +++ b/src/values/image.rs @@ -13,11 +13,13 @@ use crate::traits::{FallbackValues, Parse, ToCss}; use crate::values::string::CowArcStr; use crate::values::url::Url; use crate::vendor_prefix::VendorPrefix; +use crate::visitor::Visit; use cssparser::*; use smallvec::SmallVec; /// A CSS [``](https://www.w3.org/TR/css-images-3/#image-values) value. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] +#[visit(visit_image, IMAGES)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -339,7 +341,7 @@ impl<'i> ToCss for Image<'i> { /// /// `image-set()` allows the user agent to choose between multiple versions of an image to /// display the most appropriate resolution or file type that it supports. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ImageSet<'i> { /// The image options to choose from. @@ -411,10 +413,11 @@ impl<'i> ToCss for ImageSet<'i> { } /// An image option within the `image-set()` function. See [ImageSet](ImageSet). -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ImageSetOption<'i> { /// The image for this option. + #[skip_type] pub image: Image<'i>, /// The resolution of the image. pub resolution: Resolution, diff --git a/src/values/length.rs b/src/values/length.rs index 0f6ebfac..45f09096 100644 --- a/src/values/length.rs +++ b/src/values/length.rs @@ -11,6 +11,7 @@ use crate::traits::{ private::{AddInternal, TryAdd}, Map, Parse, Sign, ToCss, TryMap, TryOp, Zero, }; +use crate::visitor::Visit; use const_str; use cssparser::*; @@ -36,7 +37,7 @@ impl LengthPercentage { } /// Either a [``](https://www.w3.org/TR/css-values-4/#typedef-length-percentage), or the `auto` keyword. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -92,7 +93,8 @@ macro_rules! define_length_units { ) => { /// A CSS [``](https://www.w3.org/TR/css-values-4/#lengths) value, /// without support for `calc()`. See also: [Length](Length). - #[derive(Debug, Clone, PartialEq)] + #[derive(Debug, Clone, PartialEq, Visit)] + #[visit(visit_length, LENGTHS)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(tag = "unit", content = "value", rename_all = "kebab-case"))] pub enum LengthValue { $( @@ -454,7 +456,7 @@ impl LengthValue { } /// A CSS [``](https://www.w3.org/TR/css-values-4/#lengths) value, with support for `calc()`. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -464,6 +466,7 @@ pub enum Length { /// An explicitly specified length value. Value(LengthValue), /// A computed length value using `calc()`. + #[skip_type] Calc(Box>), } @@ -715,7 +718,7 @@ impl TrySign for Length { impl_try_from_angle!(Length); /// Either a [``](https://www.w3.org/TR/css-values-4/#lengths) or a [``](https://www.w3.org/TR/css-values-4/#numbers). -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), diff --git a/src/values/percentage.rs b/src/values/percentage.rs index 65fd5860..e6c81edc 100644 --- a/src/values/percentage.rs +++ b/src/values/percentage.rs @@ -7,13 +7,14 @@ use crate::error::{ParserError, PrinterError}; use crate::printer::Printer; use crate::traits::private::AddInternal; use crate::traits::{impl_op, private::TryAdd, Op, Parse, Sign, ToCss, TryMap, TryOp, TrySign, Zero}; +use crate::visitor::Visit; use cssparser::*; /// A CSS [``](https://www.w3.org/TR/css-values-4/#percentages) value. /// /// Percentages may be explicit or computed by `calc()`, but are always stored and serialized /// as their computed value. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Percentage(pub CSSNumber); @@ -139,7 +140,7 @@ impl_op!(Percentage, std::ops::Add, add); impl_try_from_angle!(Percentage); /// Either a `` or ``. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -191,7 +192,7 @@ impl std::convert::Into for &NumberOrPercentage { /// used standalone or mixed within a `calc()` expression. /// /// -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -203,6 +204,7 @@ pub enum DimensionPercentage { /// A percentage. Percentage(Percentage), /// A `calc()` expression. + #[skip_type] Calc(Box>>), } diff --git a/src/values/position.rs b/src/values/position.rs index a8b36c84..9c6b2932 100644 --- a/src/values/position.rs +++ b/src/values/position.rs @@ -6,11 +6,12 @@ use crate::error::{ParserError, PrinterError}; use crate::macros::enum_property; use crate::printer::Printer; use crate::traits::{Parse, ToCss, Zero}; +use crate::visitor::Visit; use cssparser::*; /// A CSS [``](https://www.w3.org/TR/css3-values/#position) value, /// as used in the `background-position` property, gradients, masks, etc. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Position { /// The x-position. @@ -236,7 +237,7 @@ impl ToCss for Position { /// along either the horizontal or vertical axis of a box. /// /// This type is generic over side keywords. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), diff --git a/src/values/ratio.rs b/src/values/ratio.rs index ba6d5c17..4cb7f3c3 100644 --- a/src/values/ratio.rs +++ b/src/values/ratio.rs @@ -4,11 +4,13 @@ use super::number::CSSNumber; use crate::error::{ParserError, PrinterError}; use crate::printer::Printer; use crate::traits::{Parse, ToCss}; +use crate::visitor::Visit; use cssparser::*; /// A CSS [``](https://www.w3.org/TR/css-values-4/#ratios) value, /// representing the ratio of two numeric values. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] +#[visit(visit_ratio, RATIOS)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Ratio(pub CSSNumber, pub CSSNumber); diff --git a/src/values/rect.rs b/src/values/rect.rs index 303110cf..76cd418a 100644 --- a/src/values/rect.rs +++ b/src/values/rect.rs @@ -3,6 +3,7 @@ use crate::error::{ParserError, PrinterError}; use crate::printer::Printer; use crate::traits::{Parse, ToCss}; +use crate::visitor::Visit; use cssparser::*; /// A generic value that represents a value for four sides of a box, @@ -10,7 +11,7 @@ use cssparser::*; /// /// When serialized, as few components as possible are written when /// there are duplicate values. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Rect( /// The top component. diff --git a/src/values/resolution.rs b/src/values/resolution.rs index cdbd6f64..991b1547 100644 --- a/src/values/resolution.rs +++ b/src/values/resolution.rs @@ -6,10 +6,12 @@ use crate::compat::Feature; use crate::error::{ParserError, PrinterError}; use crate::printer::Printer; use crate::traits::{Parse, ToCss}; +use crate::visitor::Visit; use cssparser::*; /// A CSS [``](https://www.w3.org/TR/css-values-4/#resolution) value. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] +#[visit(visit_resolution, RESOLUTIONS)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), diff --git a/src/values/shape.rs b/src/values/shape.rs index 7d9bda4b..6ea552dd 100644 --- a/src/values/shape.rs +++ b/src/values/shape.rs @@ -8,10 +8,11 @@ use crate::macros::enum_property; use crate::printer::Printer; use crate::properties::border_radius::BorderRadius; use crate::traits::{Parse, ToCss}; +use crate::visitor::Visit; use cssparser::*; /// A CSS [``](https://www.w3.org/TR/css-shapes-1/#basic-shape-functions) value. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -29,7 +30,7 @@ pub enum BasicShape { } /// An [`inset()`](https://www.w3.org/TR/css-shapes-1/#funcdef-inset) rectangle shape. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct InsetRect { /// The rectangle. @@ -39,7 +40,7 @@ pub struct InsetRect { } /// A [`circle()`](https://www.w3.org/TR/css-shapes-1/#funcdef-circle) shape. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Circle { /// The radius of the circle. @@ -50,7 +51,7 @@ pub struct Circle { /// A [``](https://www.w3.org/TR/css-shapes-1/#typedef-shape-radius) value /// that defines the radius of a `circle()` or `ellipse()` shape. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -66,7 +67,7 @@ pub enum ShapeRadius { } /// An [`ellipse()`](https://www.w3.org/TR/css-shapes-1/#funcdef-ellipse) shape. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Ellipse { /// The x-radius of the ellipse. @@ -78,7 +79,7 @@ pub struct Ellipse { } /// A [`polygon()`](https://www.w3.org/TR/css-shapes-1/#funcdef-polygon) shape. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Polygon { /// The fill rule used to determine the interior of the polygon. @@ -90,7 +91,7 @@ pub struct Polygon { /// A point within a `polygon()` shape. /// /// See [Polygon](Polygon). -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Point { /// The x position of the point. diff --git a/src/values/size.rs b/src/values/size.rs index f95af57a..e89a3f74 100644 --- a/src/values/size.rs +++ b/src/values/size.rs @@ -3,12 +3,13 @@ use crate::error::{ParserError, PrinterError}; use crate::printer::Printer; use crate::traits::{Parse, ToCss}; +use crate::visitor::Visit; use cssparser::*; /// A generic value that represents a value with two components, e.g. a border radius. /// /// When serialized, only a single component will be written if both are equal. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Visit)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Size2D(pub T, pub T); diff --git a/src/values/string.rs b/src/values/string.rs index 7b60a684..022a9bb6 100644 --- a/src/values/string.rs +++ b/src/values/string.rs @@ -1,8 +1,9 @@ //! Types used to represent strings. +use crate::visitor::{Visit, VisitTypes, Visitor}; use cssparser::CowRcStr; #[cfg(feature = "serde")] -use serde::{de::Visitor, Deserialize, Deserializer}; +use serde::{Deserialize, Deserializer}; use serde::{Serialize, Serializer}; use std::borrow::Borrow; use std::cmp; @@ -237,7 +238,7 @@ impl<'a, 'de: 'a> Deserialize<'de> for CowArcStr<'a> { struct CowArcStrVisitor; #[cfg(feature = "serde")] -impl<'de> Visitor<'de> for CowArcStrVisitor { +impl<'de> serde::de::Visitor<'de> for CowArcStrVisitor { type Value = CowArcStr<'de>; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { @@ -265,3 +266,8 @@ impl<'de> Visitor<'de> for CowArcStrVisitor { Ok(v.into()) } } + +impl<'i, V: Visitor<'i, T>, T> Visit<'i, T, V> for CowArcStr<'i> { + const CHILD_TYPES: VisitTypes = VisitTypes::empty(); + fn visit_children(&mut self, _: &mut V) {} +} diff --git a/src/values/time.rs b/src/values/time.rs index 5d3f1255..b3a696fc 100644 --- a/src/values/time.rs +++ b/src/values/time.rs @@ -7,6 +7,7 @@ use crate::error::{ParserError, PrinterError}; use crate::printer::Printer; use crate::traits::private::AddInternal; use crate::traits::{impl_op, Map, Op, Parse, Sign, ToCss, Zero}; +use crate::visitor::Visit; use cssparser::*; /// A CSS [`