stylo_derive/
specified_value_info.rs1use crate::cg;
6use crate::parse::ParseVariantAttrs;
7use crate::to_css::{CssFieldAttrs, CssInputAttrs, CssVariantAttrs};
8use proc_macro2::TokenStream;
9use quote::TokenStreamExt;
10use syn::{Data, DeriveInput, Fields, Ident, Type};
11
12pub 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 } 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
137fn 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)]
179struct ValueInfoInputAttrs {
180 ty: Option<Ident>,
181 other_values: Option<String>,
182}
183
184#[derive(Default, FromVariant)]
185#[darling(attributes(value_info), default)]
186struct ValueInfoVariantAttrs {
187 starts_with_keyword: bool,
188 other_values: Option<String>,
189}
190
191#[derive(Default, FromField)]
192#[darling(attributes(value_info), default)]
193struct ValueInfoFieldAttrs {
194 other_values: Option<String>,
195}