diff --git a/servo/components/style/counter_style/mod.rs b/servo/components/style/counter_style/mod.rs index 397598d45a75..e727e004ebda 100644 --- a/servo/components/style/counter_style/mod.rs +++ b/servo/components/style/counter_style/mod.rs @@ -547,9 +547,8 @@ impl Parse for Fallback { /// #[cfg_attr(feature = "gecko", derive(MallocSizeOf))] -#[css(iterable)] #[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToCss)] -pub struct Symbols(pub Vec); +pub struct Symbols(#[css(iterable)] pub Vec); impl Parse for Symbols { fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result> { diff --git a/servo/components/style/media_queries.rs b/servo/components/style/media_queries.rs index e6fb46147624..0232916fc280 100644 --- a/servo/components/style/media_queries.rs +++ b/servo/components/style/media_queries.rs @@ -25,10 +25,11 @@ pub use gecko::media_queries::{Device, Expression}; /// A type that encapsulates a media query list. #[cfg_attr(feature = "servo", derive(MallocSizeOf))] -#[css(comma, iterable)] +#[css(comma)] #[derive(Clone, Debug, ToCss)] pub struct MediaList { /// The list of media queries. + #[css(iterable)] pub media_queries: Vec, } diff --git a/servo/components/style/properties/helpers.mako.rs b/servo/components/style/properties/helpers.mako.rs index 6e6c15d981fb..1be21cd377c7 100644 --- a/servo/components/style/properties/helpers.mako.rs +++ b/servo/components/style/properties/helpers.mako.rs @@ -126,9 +126,7 @@ % endif % if not allow_empty: % if separator == "Comma": - #[css(comma, iterable)] - % else: - #[css(iterable)] + #[css(comma)] % endif #[derive(ToCss)] % endif @@ -136,6 +134,9 @@ % if allow_empty and allow_empty != "NotInitial": pub Vec, % else: + % if not allow_empty: + #[css(iterable)] + % endif pub SmallVec<[single_value::T; 1]>, % endif ); @@ -189,13 +190,16 @@ #[derive(Clone, Debug, MallocSizeOf, PartialEq)] % if not allow_empty: % if separator == "Comma": - #[css(comma, iterable)] - % else: - #[css(iterable)] + #[css(comma)] % endif #[derive(ToCss)] % endif - pub struct SpecifiedValue(pub Vec); + pub struct SpecifiedValue( + % if not allow_empty: + #[css(iterable)] + % endif + pub Vec, + ); % if allow_empty: impl ToCss for SpecifiedValue { diff --git a/servo/components/style/stylesheets/document_rule.rs b/servo/components/style/stylesheets/document_rule.rs index 104f13ec7106..8c9a3e21ee65 100644 --- a/servo/components/style/stylesheets/document_rule.rs +++ b/servo/components/style/stylesheets/document_rule.rs @@ -176,9 +176,9 @@ impl UrlMatchingFunction { /// The `@document` rule's condition is written as a comma-separated list of /// URL matching functions, and the condition evaluates to true whenever any /// one of those functions evaluates to true. -#[css(comma, iterable)] +#[css(comma)] #[derive(Clone, Debug, ToCss)] -pub struct DocumentCondition(Vec); +pub struct DocumentCondition(#[css(iterable)] Vec); impl DocumentCondition { /// Parse a document condition. diff --git a/servo/components/style/stylesheets/font_feature_values_rule.rs b/servo/components/style/stylesheets/font_feature_values_rule.rs index f27ac9b6d3d6..eb799793f791 100644 --- a/servo/components/style/stylesheets/font_feature_values_rule.rs +++ b/servo/components/style/stylesheets/font_feature_values_rule.rs @@ -118,9 +118,8 @@ impl ToGeckoFontFeatureValues for PairValues { } /// A @font-feature-values block declaration value that keeps a list of values. -#[css(iterable)] #[derive(Clone, Debug, PartialEq, ToCss)] -pub struct VectorValues(pub Vec); +pub struct VectorValues(#[css(iterable)] pub Vec); impl Parse for VectorValues { fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>) diff --git a/servo/components/style/stylesheets/keyframes_rule.rs b/servo/components/style/stylesheets/keyframes_rule.rs index aeb9265c549a..266d953cd996 100644 --- a/servo/components/style/stylesheets/keyframes_rule.rs +++ b/servo/components/style/stylesheets/keyframes_rule.rs @@ -145,9 +145,9 @@ impl KeyframePercentage { /// A keyframes selector is a list of percentages or from/to symbols, which are /// converted at parse time to percentages. -#[css(comma, iterable)] +#[css(comma)] #[derive(Clone, Debug, Eq, PartialEq, ToCss)] -pub struct KeyframeSelector(Vec); +pub struct KeyframeSelector(#[css(iterable)] Vec); impl KeyframeSelector { /// Return the list of percentages this selector contains. diff --git a/servo/components/style/values/specified/box.rs b/servo/components/style/values/specified/box.rs index 731b0ee18d65..973708561bcc 100644 --- a/servo/components/style/values/specified/box.rs +++ b/servo/components/style/values/specified/box.rs @@ -369,9 +369,9 @@ pub enum OverflowClipBox { pub enum WillChange { /// Expresses no particular intent Auto, - #[css(comma, iterable)] /// - AnimateableFeatures(Box<[CustomIdent]>), + #[css(comma)] + AnimateableFeatures(#[css(iterable)] Box<[CustomIdent]>), } impl WillChange { diff --git a/servo/components/style/values/specified/counters.rs b/servo/components/style/values/specified/counters.rs index 9460f3965cf6..3252001f142a 100644 --- a/servo/components/style/values/specified/counters.rs +++ b/servo/components/style/values/specified/counters.rs @@ -92,8 +92,7 @@ pub enum Content { #[cfg(feature = "gecko")] MozAltContent, /// Content items. - #[css(iterable)] - Items(Box<[ContentItem]>), + Items(#[css(iterable)] Box<[ContentItem]>), } /// Items for the `content` property. diff --git a/servo/components/style/values/specified/font.rs b/servo/components/style/values/specified/font.rs index 8b2cf9737b9b..7d442b491267 100644 --- a/servo/components/style/values/specified/font.rs +++ b/servo/components/style/values/specified/font.rs @@ -152,8 +152,8 @@ impl From for FontSize { #[derive(Clone, Debug, Eq, Hash, PartialEq, ToCss)] pub enum FontFamily { /// List of `font-family` - #[css(iterable, comma)] - Values(FontFamilyList), + #[css(comma)] + Values(#[css(iterable)] FontFamilyList), /// System font System(SystemFont), } @@ -718,11 +718,11 @@ pub enum VariantAlternates { #[css(function)] Stylistic(CustomIdent), /// Enables display with stylistic sets - #[css(comma, function, iterable)] - Styleset(Box<[CustomIdent]>), + #[css(comma, function)] + Styleset(#[css(iterable)] Box<[CustomIdent]>), /// Enables display of specific character variants - #[css(comma, function, iterable)] - CharacterVariant(Box<[CustomIdent]>), + #[css(comma, function)] + CharacterVariant(#[css(iterable)] Box<[CustomIdent]>), /// Enables display of swash glyphs #[css(function)] Swash(CustomIdent), diff --git a/servo/components/style_derive/to_css.rs b/servo/components/style_derive/to_css.rs index 5d9654de3505..4e394ba605b6 100644 --- a/servo/components/style_derive/to_css.rs +++ b/servo/components/style_derive/to_css.rs @@ -2,96 +2,27 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use cg; +use cg::{self, WhereClause}; use darling::util::Override; -use quote::Tokens; -use syn::{self, Ident}; -use synstructure; +use quote::{ToTokens, Tokens}; +use syn::{self, Data}; +use synstructure::{BindingInfo, Structure, VariantInfo}; pub fn derive(input: syn::DeriveInput) -> Tokens { let name = &input.ident; - let trait_path = parse_quote!(style_traits::ToCss); + let trait_path = parse_quote!(::style_traits::ToCss); let (impl_generics, ty_generics, mut where_clause) = cg::trait_parts(&input, &trait_path); let input_attrs = cg::parse_input_attrs::(&input); - let s = synstructure::Structure::new(&input); + if let Data::Enum(_) = input.data { + assert!(input_attrs.function.is_none(), "#[css(function)] is not allowed on enums"); + assert!(!input_attrs.comma, "#[css(comma)] is not allowed on enums"); + } + let s = Structure::new(&input); let match_body = s.each_variant(|variant| { - let bindings = variant.bindings(); - let identifier = cg::to_css_identifier(variant.ast().ident.as_ref()); - let ast = variant.ast(); - let variant_attrs = cg::parse_variant_attrs::(&ast); - let separator = if variant_attrs.comma { ", " } else { " " }; - - if variant_attrs.dimension { - assert_eq!(bindings.len(), 1); - assert!( - variant_attrs.function.is_none() && variant_attrs.keyword.is_none(), - "That makes no sense" - ); - } - - let mut expr = if let Some(keyword) = variant_attrs.keyword { - assert!(bindings.is_empty()); - let keyword = keyword.to_string(); - quote! { - ::std::fmt::Write::write_str(dest, #keyword) - } - } else if !bindings.is_empty() { - let mut expr = quote! {}; - if variant_attrs.iterable { - assert_eq!(bindings.len(), 1); - let binding = &bindings[0]; - expr = quote! { - #expr - - for item in #binding.iter() { - writer.item(&item)?; - } - }; - } else { - for binding in bindings { - let attrs = cg::parse_field_attrs::(&binding.ast()); - if attrs.skip { - continue; - } - if !attrs.ignore_bound { - where_clause.add_trait_bound(&binding.ast().ty); - } - expr = quote! { - #expr - writer.item(#binding)?; - }; - } - } - - quote! {{ - let mut writer = ::style_traits::values::SequenceWriter::new(dest, #separator); - #expr - Ok(()) - }} - } else { - quote! { - ::std::fmt::Write::write_str(dest, #identifier) - } - }; - - if variant_attrs.dimension { - expr = quote! { - #expr?; - ::std::fmt::Write::write_str(dest, #identifier) - } - } else if let Some(function) = variant_attrs.function { - let mut identifier = function.explicit().map_or(identifier, |name| name.to_string()); - identifier.push_str("("); - expr = quote! { - ::std::fmt::Write::write_str(dest, #identifier)?; - #expr?; - ::std::fmt::Write::write_str(dest, ")") - } - } - Some(expr) + derive_variant_arm(variant, &mut where_clause) }); let mut impls = quote! { @@ -128,23 +59,124 @@ pub fn derive(input: syn::DeriveInput) -> Tokens { impls } +fn derive_variant_arm( + variant: &VariantInfo, + where_clause: &mut WhereClause, +) -> Tokens { + let bindings = variant.bindings(); + let identifier = cg::to_css_identifier(variant.ast().ident.as_ref()); + let ast = variant.ast(); + let variant_attrs = cg::parse_variant_attrs::(&ast); + let separator = if variant_attrs.comma { ", " } else { " " }; + + if variant_attrs.dimension { + assert_eq!(bindings.len(), 1); + assert!( + variant_attrs.function.is_none() && variant_attrs.keyword.is_none(), + "That makes no sense" + ); + } + + let mut expr = if let Some(keyword) = variant_attrs.keyword { + assert!(bindings.is_empty()); + let keyword = keyword.to_string(); + quote! { + ::std::fmt::Write::write_str(dest, #keyword) + } + } else if !bindings.is_empty() { + derive_variant_fields_expr(bindings, where_clause, separator) + } else { + quote! { + ::std::fmt::Write::write_str(dest, #identifier) + } + }; + + if variant_attrs.dimension { + expr = quote! { + #expr?; + ::std::fmt::Write::write_str(dest, #identifier) + } + } else if let Some(function) = variant_attrs.function { + let mut identifier = function.explicit().map_or(identifier, |name| name); + identifier.push_str("("); + expr = quote! { + ::std::fmt::Write::write_str(dest, #identifier)?; + #expr?; + ::std::fmt::Write::write_str(dest, ")") + } + } + expr +} + +fn derive_variant_fields_expr( + bindings: &[BindingInfo], + where_clause: &mut WhereClause, + separator: &str, +) -> Tokens { + let mut iter = bindings.iter().filter_map(|binding| { + let attrs = cg::parse_field_attrs::(&binding.ast()); + if attrs.skip { + return None; + } + Some((binding, attrs)) + }).peekable(); + + let (first, attrs) = match iter.next() { + Some(pair) => pair, + None => return quote! { Ok(()) }, + }; + if !attrs.iterable && iter.peek().is_none() { + if !attrs.ignore_bound { + where_clause.add_trait_bound(&first.ast().ty); + } + return quote! { ::style_traits::ToCss::to_css(#first, dest) }; + } + + let mut expr = derive_single_field_expr(first, attrs, where_clause); + for (binding, attrs) in iter { + derive_single_field_expr(binding, attrs, where_clause).to_tokens(&mut expr) + } + + quote! {{ + let mut writer = ::style_traits::values::SequenceWriter::new(dest, #separator); + #expr + Ok(()) + }} +} + +fn derive_single_field_expr( + field: &BindingInfo, + attrs: CssFieldAttrs, + where_clause: &mut WhereClause, +) -> Tokens { + if attrs.iterable { + quote! { + for item in #field.iter() { + writer.item(&item)?; + } + } + } else { + if !attrs.ignore_bound { + where_clause.add_trait_bound(&field.ast().ty); + } + quote! { writer.item(#field)?; } + } +} + #[darling(attributes(css), default)] #[derive(Default, FromDeriveInput)] struct CssInputAttrs { derive_debug: bool, // Here because structs variants are also their whole type definition. - function: Option>, + function: Option>, // Here because structs variants are also their whole type definition. comma: bool, - // Here because structs variants are also their whole type definition. - iterable: bool, } #[darling(attributes(css), default)] #[derive(Default, FromVariant)] pub struct CssVariantAttrs { - pub function: Option>, - pub iterable: bool, + pub function: Option>, pub comma: bool, pub dimension: bool, pub keyword: Option, @@ -155,5 +187,6 @@ pub struct CssVariantAttrs { #[derive(Default, FromField)] struct CssFieldAttrs { ignore_bound: bool, + iterable: bool, skip: bool, }