1use darling::{FromDeriveInput, FromField, FromVariant};
6use proc_macro2::{Span, TokenStream};
7use quote::{quote, TokenStreamExt};
8use syn::parse_quote;
9use syn::{self, AngleBracketedGenericArguments, AssocType, DeriveInput, Field};
10use syn::{GenericArgument, GenericParam, Ident, Path};
11use syn::{PathArguments, PathSegment, QSelf, Type, TypeArray, TypeGroup};
12use syn::{TypeParam, TypeParen, TypePath, TypeSlice, TypeTuple};
13use syn::{Variant, WherePredicate};
14use synstructure::{self, BindStyle, BindingInfo, VariantAst, VariantInfo};
15
16pub(crate) fn propagate_clauses_to_output_type(
36 where_clause: &mut Option<syn::WhereClause>,
37 generics: &syn::Generics,
38 trait_path: &Path,
39 trait_output: &Ident,
40) {
41 let where_clause = match *where_clause {
42 Some(ref mut clause) => clause,
43 None => return,
44 };
45 let mut extra_bounds = vec![];
46 for pred in &where_clause.predicates {
47 let ty = match *pred {
48 syn::WherePredicate::Type(ref ty) => ty,
49 ref predicate => panic!("Unhanded complex where predicate: {:?}", predicate),
50 };
51
52 let path = match ty.bounded_ty {
53 syn::Type::Path(ref p) => &p.path,
54 ref ty => panic!("Unhanded complex where type: {:?}", ty),
55 };
56
57 assert!(
58 ty.lifetimes.is_none(),
59 "Unhanded complex lifetime bound: {:?}",
60 ty,
61 );
62
63 let ident = match path_to_ident(path) {
64 Some(i) => i,
65 None => panic!("Unhanded complex where type path: {:?}", path),
66 };
67
68 if generics.type_params().any(|param| param.ident == *ident) {
69 extra_bounds.push(ty.clone());
70 }
71 }
72
73 for bound in extra_bounds {
74 let ty = bound.bounded_ty;
75 let bounds = bound.bounds;
76 where_clause
77 .predicates
78 .push(parse_quote!(<#ty as #trait_path>::#trait_output: #bounds))
79 }
80}
81
82pub(crate) fn add_predicate(where_clause: &mut Option<syn::WhereClause>, pred: WherePredicate) {
83 where_clause
84 .get_or_insert(parse_quote!(where))
85 .predicates
86 .push(pred);
87}
88
89pub(crate) fn fmap_match<F>(input: &DeriveInput, bind_style: BindStyle, f: F) -> TokenStream
90where
91 F: FnMut(&BindingInfo) -> TokenStream,
92{
93 fmap2_match(input, bind_style, f, |_| None)
94}
95
96pub(crate) fn fmap2_match<F, G>(
97 input: &DeriveInput,
98 bind_style: BindStyle,
99 mut f: F,
100 mut g: G,
101) -> TokenStream
102where
103 F: FnMut(&BindingInfo) -> TokenStream,
104 G: FnMut(&BindingInfo) -> Option<TokenStream>,
105{
106 let mut s = synstructure::Structure::new(input);
107 s.variants_mut().iter_mut().for_each(|v| {
108 v.bind_with(|_| bind_style);
109 });
110 s.each_variant(|variant| {
111 let (mapped, mapped_fields) = value(variant, "mapped");
112 let fields_pairs = variant.bindings().iter().zip(mapped_fields.iter());
113 let mut computations = quote!();
114 computations.append_all(fields_pairs.map(|(field, mapped_field)| {
115 let expr = f(field);
116 quote! { let #mapped_field = #expr; }
117 }));
118 computations.append_all(
119 mapped_fields
120 .iter()
121 .map(|mapped_field| match g(mapped_field) {
122 Some(expr) => quote! { let #mapped_field = #expr; },
123 None => quote!(),
124 }),
125 );
126 computations.append_all(mapped);
127 Some(computations)
128 })
129}
130
131pub(crate) fn fmap_trait_output(
132 input: &DeriveInput,
133 trait_path: &Path,
134 trait_output: &Ident,
135) -> Path {
136 let segment = PathSegment {
137 ident: input.ident.clone(),
138 arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments {
139 args: input
140 .generics
141 .params
142 .iter()
143 .map(|arg| match arg {
144 &GenericParam::Lifetime(ref data) => {
145 GenericArgument::Lifetime(data.lifetime.clone())
146 },
147 &GenericParam::Type(ref data) => {
148 let ident = &data.ident;
149 GenericArgument::Type(parse_quote!(<#ident as #trait_path>::#trait_output))
150 },
151 &GenericParam::Const(ref inner) => {
152 let ident = &inner.ident;
153 GenericArgument::Const(parse_quote!(#ident))
154 },
155 })
156 .collect(),
157 colon2_token: Default::default(),
158 gt_token: Default::default(),
159 lt_token: Default::default(),
160 }),
161 };
162 segment.into()
163}
164
165pub(crate) fn map_type_params<F>(
166 ty: &Type,
167 params: &[&TypeParam],
168 self_type: &Path,
169 f: &mut F,
170) -> Type
171where
172 F: FnMut(&Ident) -> Type,
173{
174 match *ty {
175 Type::Slice(ref inner) => Type::from(TypeSlice {
176 elem: Box::new(map_type_params(&inner.elem, params, self_type, f)),
177 ..inner.clone()
178 }),
179 Type::Array(ref inner) => {
180 Type::from(TypeArray {
182 elem: Box::new(map_type_params(&inner.elem, params, self_type, f)),
183 ..inner.clone()
184 })
185 },
186 ref ty @ Type::Never(_) => ty.clone(),
187 Type::Tuple(ref inner) => Type::from(TypeTuple {
188 elems: inner
189 .elems
190 .iter()
191 .map(|ty| map_type_params(&ty, params, self_type, f))
192 .collect(),
193 ..inner.clone()
194 }),
195 Type::Path(TypePath {
196 qself: None,
197 ref path,
198 }) => {
199 if let Some(ident) = path_to_ident(path) {
200 if params.iter().any(|ref param| ¶m.ident == ident) {
201 return f(ident);
202 }
203 if ident == "Self" {
204 return Type::from(TypePath {
205 qself: None,
206 path: self_type.clone(),
207 });
208 }
209 }
210 Type::from(TypePath {
211 qself: None,
212 path: map_type_params_in_path(path, params, self_type, f),
213 })
214 },
215 Type::Path(TypePath {
216 ref qself,
217 ref path,
218 }) => Type::from(TypePath {
219 qself: qself.as_ref().map(|qself| QSelf {
220 ty: Box::new(map_type_params(&qself.ty, params, self_type, f)),
221 position: qself.position,
222 ..qself.clone()
223 }),
224 path: map_type_params_in_path(path, params, self_type, f),
225 }),
226 Type::Paren(ref inner) => Type::from(TypeParen {
227 elem: Box::new(map_type_params(&inner.elem, params, self_type, f)),
228 ..inner.clone()
229 }),
230 Type::Group(ref inner) => Type::from(TypeGroup {
231 elem: Box::new(map_type_params(&inner.elem, params, self_type, f)),
232 ..inner.clone()
233 }),
234 ref ty => panic!("type {:?} cannot be mapped yet", ty),
235 }
236}
237
238fn map_type_params_in_path<F>(
239 path: &Path,
240 params: &[&TypeParam],
241 self_type: &Path,
242 f: &mut F,
243) -> Path
244where
245 F: FnMut(&Ident) -> Type,
246{
247 Path {
248 leading_colon: path.leading_colon,
249 segments: path
250 .segments
251 .iter()
252 .map(|segment| PathSegment {
253 ident: segment.ident.clone(),
254 arguments: match segment.arguments {
255 PathArguments::AngleBracketed(ref data) => {
256 PathArguments::AngleBracketed(AngleBracketedGenericArguments {
257 args: data
258 .args
259 .iter()
260 .map(|arg| match arg {
261 ty @ &GenericArgument::Lifetime(_) => ty.clone(),
262 &GenericArgument::Type(ref data) => GenericArgument::Type(
263 map_type_params(data, params, self_type, f),
264 ),
265 &GenericArgument::AssocType(ref data) => {
266 GenericArgument::AssocType(AssocType {
267 ty: map_type_params(&data.ty, params, self_type, f),
268 ..data.clone()
269 })
270 },
271 ref arg => panic!("arguments {:?} cannot be mapped yet", arg),
272 })
273 .collect(),
274 ..data.clone()
275 })
276 },
277 ref arg @ PathArguments::None => arg.clone(),
278 ref parameters => panic!("parameters {:?} cannot be mapped yet", parameters),
279 },
280 })
281 .collect(),
282 }
283}
284
285fn path_to_ident(path: &Path) -> Option<&Ident> {
286 match *path {
287 Path {
288 leading_colon: None,
289 ref segments,
290 } if segments.len() == 1 => {
291 if segments[0].arguments.is_empty() {
292 Some(&segments[0].ident)
293 } else {
294 None
295 }
296 },
297 _ => None,
298 }
299}
300
301pub(crate) fn parse_field_attrs<A>(field: &Field) -> A
302where
303 A: FromField,
304{
305 match A::from_field(field) {
306 Ok(attrs) => attrs,
307 Err(e) => panic!("failed to parse field attributes: {}", e),
308 }
309}
310
311pub(crate) fn parse_input_attrs<A>(input: &DeriveInput) -> A
312where
313 A: FromDeriveInput,
314{
315 match A::from_derive_input(input) {
316 Ok(attrs) => attrs,
317 Err(e) => panic!("failed to parse input attributes: {}", e),
318 }
319}
320
321pub(crate) fn parse_variant_attrs_from_ast<A>(variant: &VariantAst) -> A
322where
323 A: FromVariant,
324{
325 let v = Variant {
326 ident: variant.ident.clone(),
327 attrs: variant.attrs.to_vec(),
328 fields: variant.fields.clone(),
329 discriminant: variant.discriminant.clone(),
330 };
331 parse_variant_attrs(&v)
332}
333
334pub(crate) fn parse_variant_attrs<A>(variant: &Variant) -> A
335where
336 A: FromVariant,
337{
338 match A::from_variant(variant) {
339 Ok(attrs) => attrs,
340 Err(e) => panic!("failed to parse variant attributes: {}", e),
341 }
342}
343
344pub(crate) fn ref_pattern<'a>(
345 variant: &'a VariantInfo,
346 prefix: &str,
347) -> (TokenStream, Vec<BindingInfo<'a>>) {
348 let mut v = variant.clone();
349 v.bind_with(|_| BindStyle::Ref);
350 v.bindings_mut().iter_mut().for_each(|b| {
351 b.binding = Ident::new(&format!("{}_{}", b.binding, prefix), Span::call_site())
352 });
353 (v.pat(), v.bindings().to_vec())
354}
355
356pub(crate) fn value<'a>(
357 variant: &'a VariantInfo,
358 prefix: &str,
359) -> (TokenStream, Vec<BindingInfo<'a>>) {
360 let mut v = variant.clone();
361 v.bindings_mut().iter_mut().for_each(|b| {
362 b.binding = Ident::new(&format!("{}_{}", b.binding, prefix), Span::call_site())
363 });
364 v.bind_with(|_| BindStyle::Move);
365 (v.pat(), v.bindings().to_vec())
366}
367
368pub(crate) fn to_css_identifier(mut camel_case: &str) -> String {
373 camel_case = camel_case.trim_end_matches('_');
374 let mut first = true;
375 let mut result = String::with_capacity(camel_case.len());
376 while let Some(segment) = split_camel_segment(&mut camel_case) {
377 if first {
378 match segment {
379 "Moz" | "Webkit" | "Servo" => first = false,
380 _ => {},
381 }
382 }
383 if !first {
384 result.push('-');
385 }
386 first = false;
387 result.push_str(&segment.to_lowercase());
388 }
389 result
390}
391
392pub(crate) fn to_scream_case(css_case: &str) -> String {
394 css_case.to_uppercase().replace('-', "_")
395}
396
397fn split_camel_segment<'input>(camel_case: &mut &'input str) -> Option<&'input str> {
399 let index = match camel_case.chars().next() {
400 None => return None,
401 Some(ch) => ch.len_utf8(),
402 };
403 let end_position = camel_case[index..]
404 .find(char::is_uppercase)
405 .map_or(camel_case.len(), |pos| index + pos);
406 let result = &camel_case[..end_position];
407 *camel_case = &camel_case[end_position..];
408 Some(result)
409}