specified_value_info.rs (7014B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 4 5 use crate::cg; 6 use crate::parse::ParseVariantAttrs; 7 use crate::to_css::{CssFieldAttrs, CssInputAttrs, CssVariantAttrs}; 8 use proc_macro2::TokenStream; 9 use quote::TokenStreamExt; 10 use syn::{Data, DeriveInput, Fields, Ident, Type}; 11 12 pub fn derive(mut input: DeriveInput) -> TokenStream { 13 let css_attrs = cg::parse_input_attrs::<CssInputAttrs>(&input); 14 let mut types = vec![]; 15 let mut values = vec![]; 16 17 let input_ident = &input.ident; 18 let input_name = || cg::to_css_identifier(&input_ident.to_string()); 19 if let Some(function) = css_attrs.function { 20 values.push(function.explicit().unwrap_or_else(input_name)); 21 // If the whole value is wrapped in a function, value types of 22 // its fields should not be propagated. 23 } else { 24 let mut where_clause = input.generics.where_clause.take(); 25 for param in input.generics.type_params() { 26 cg::add_predicate( 27 &mut where_clause, 28 parse_quote!(#param: style_traits::SpecifiedValueInfo), 29 ); 30 } 31 input.generics.where_clause = where_clause; 32 33 match input.data { 34 Data::Enum(ref e) => { 35 for v in e.variants.iter() { 36 let css_attrs = cg::parse_variant_attrs::<CssVariantAttrs>(&v); 37 let info_attrs = cg::parse_variant_attrs::<ValueInfoVariantAttrs>(&v); 38 let parse_attrs = cg::parse_variant_attrs::<ParseVariantAttrs>(&v); 39 if css_attrs.skip { 40 continue; 41 } 42 if let Some(aliases) = parse_attrs.aliases { 43 for alias in aliases.split(',') { 44 values.push(alias.to_string()); 45 } 46 } 47 if let Some(other_values) = info_attrs.other_values { 48 for value in other_values.split(',') { 49 values.push(value.to_string()); 50 } 51 } 52 let ident = &v.ident; 53 let variant_name = || cg::to_css_identifier(&ident.to_string()); 54 if info_attrs.starts_with_keyword { 55 values.push(variant_name()); 56 continue; 57 } 58 if let Some(keyword) = css_attrs.keyword { 59 values.push(keyword); 60 continue; 61 } 62 if let Some(function) = css_attrs.function { 63 values.push(function.explicit().unwrap_or_else(variant_name)); 64 } else if !derive_struct_fields(&v.fields, &mut types, &mut values) { 65 values.push(variant_name()); 66 } 67 } 68 }, 69 Data::Struct(ref s) => { 70 if let Some(ref bitflags) = css_attrs.bitflags { 71 for (_rust_name, css_name) in bitflags.single_flags() { 72 values.push(css_name) 73 } 74 for (_rust_name, css_name) in bitflags.mixed_flags() { 75 values.push(css_name) 76 } 77 } else if !derive_struct_fields(&s.fields, &mut types, &mut values) { 78 values.push(input_name()); 79 } 80 }, 81 Data::Union(_) => unreachable!("union is not supported"), 82 } 83 } 84 85 let info_attrs = cg::parse_input_attrs::<ValueInfoInputAttrs>(&input); 86 if let Some(other_values) = info_attrs.other_values { 87 for value in other_values.split(',') { 88 values.push(value.to_string()); 89 } 90 } 91 92 let mut types_value = quote!(0); 93 types_value.append_all(types.iter().map(|ty| { 94 quote! { 95 | <#ty as style_traits::SpecifiedValueInfo>::SUPPORTED_TYPES 96 } 97 })); 98 99 let mut nested_collects = quote!(); 100 nested_collects.append_all(types.iter().map(|ty| { 101 quote! { 102 <#ty as style_traits::SpecifiedValueInfo>::collect_completion_keywords(_f); 103 } 104 })); 105 106 if let Some(ty) = info_attrs.ty { 107 types_value.append_all(quote! { 108 | style_traits::CssType::#ty 109 }); 110 } 111 112 let append_values = if values.is_empty() { 113 quote!() 114 } else { 115 let mut value_list = quote!(); 116 value_list.append_separated(values.iter(), quote! { , }); 117 quote! { _f(&[#value_list]); } 118 }; 119 120 let name = &input.ident; 121 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); 122 123 quote! { 124 impl #impl_generics style_traits::SpecifiedValueInfo for #name #ty_generics 125 #where_clause 126 { 127 const SUPPORTED_TYPES: u8 = #types_value; 128 129 fn collect_completion_keywords(_f: &mut dyn FnMut(&[&'static str])) { 130 #nested_collects 131 #append_values 132 } 133 } 134 } 135 } 136 137 /// Derive from the given fields. Return false if the fields is a Unit, 138 /// true otherwise. 139 fn derive_struct_fields<'a>( 140 fields: &'a Fields, 141 types: &mut Vec<&'a Type>, 142 values: &mut Vec<String>, 143 ) -> bool { 144 let fields = match *fields { 145 Fields::Unit => return false, 146 Fields::Named(ref fields) => fields.named.iter(), 147 Fields::Unnamed(ref fields) => fields.unnamed.iter(), 148 }; 149 types.extend(fields.filter_map(|field| { 150 let info_attrs = cg::parse_field_attrs::<ValueInfoFieldAttrs>(field); 151 if let Some(other_values) = info_attrs.other_values { 152 for value in other_values.split(',') { 153 values.push(value.to_string()); 154 } 155 } 156 let css_attrs = cg::parse_field_attrs::<CssFieldAttrs>(field); 157 if css_attrs.represents_keyword { 158 let ident = field 159 .ident 160 .as_ref() 161 .expect("only named field should use represents_keyword"); 162 values.push(cg::to_css_identifier(&ident.to_string()).replace("_", "-")); 163 return None; 164 } 165 if let Some(if_empty) = css_attrs.if_empty { 166 values.push(if_empty); 167 } 168 if !css_attrs.skip { 169 Some(&field.ty) 170 } else { 171 None 172 } 173 })); 174 true 175 } 176 177 #[derive(Default, FromDeriveInput)] 178 #[darling(attributes(value_info), default)] 179 struct ValueInfoInputAttrs { 180 ty: Option<Ident>, 181 other_values: Option<String>, 182 } 183 184 #[derive(Default, FromVariant)] 185 #[darling(attributes(value_info), default)] 186 struct ValueInfoVariantAttrs { 187 starts_with_keyword: bool, 188 other_values: Option<String>, 189 } 190 191 #[derive(Default, FromField)] 192 #[darling(attributes(value_info), default)] 193 struct ValueInfoFieldAttrs { 194 other_values: Option<String>, 195 }