macros: Simplify Incoming derive implementation
This commit is contained in:
		
							parent
							
								
									a6a530dcc8
								
							
						
					
					
						commit
						5a791b3c6e
					
				| @ -4,32 +4,49 @@ use syn::{ | ||||
|     parse::{Parse, ParseStream}, | ||||
|     parse_quote, | ||||
|     punctuated::Punctuated, | ||||
|     AngleBracketedGenericArguments, Attribute, Data, DeriveInput, Field, Fields, GenericArgument, | ||||
|     GenericParam, Generics, Ident, ImplGenerics, ParenthesizedGenericArguments, Path, | ||||
|     PathArguments, Token, Type, TypeGenerics, TypePath, TypeReference, TypeSlice, Variant, | ||||
|     AngleBracketedGenericArguments, Attribute, Data, DeriveInput, GenericArgument, GenericParam, | ||||
|     Generics, Ident, ParenthesizedGenericArguments, Path, PathArguments, Token, Type, TypePath, | ||||
|     TypeReference, TypeSlice, | ||||
| }; | ||||
| 
 | ||||
| use crate::util::import_ruma_common; | ||||
| 
 | ||||
| enum StructKind { | ||||
|     Struct, | ||||
|     Tuple, | ||||
| } | ||||
| 
 | ||||
| enum DataKind { | ||||
|     Struct(Vec<Field>, StructKind), | ||||
|     Enum(Vec<Variant>), | ||||
|     Unit, | ||||
| } | ||||
| 
 | ||||
| pub fn expand_derive_incoming(input: DeriveInput) -> syn::Result<TokenStream> { | ||||
| pub fn expand_derive_incoming(mut ty_def: DeriveInput) -> syn::Result<TokenStream> { | ||||
|     let ruma_common = import_ruma_common(); | ||||
| 
 | ||||
|     let mut found_lifetime = false; | ||||
|     match &mut ty_def.data { | ||||
|         Data::Union(_) => panic!("#[derive(Incoming)] does not support Union types"), | ||||
|         Data::Enum(e) => { | ||||
|             for var in &mut e.variants { | ||||
|                 for field in &mut var.fields { | ||||
|                     if strip_lifetimes(&mut field.ty) { | ||||
|                         found_lifetime = true; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         Data::Struct(s) => { | ||||
|             for field in &mut s.fields { | ||||
|                 if !matches!(field.vis, syn::Visibility::Public(_)) { | ||||
|                     return Err(syn::Error::new_spanned(field, "All fields must be marked `pub`")); | ||||
|                 } | ||||
|                 if strip_lifetimes(&mut field.ty) { | ||||
|                     found_lifetime = true; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if !found_lifetime { | ||||
|         return Ok(TokenStream::new()); | ||||
|     } | ||||
| 
 | ||||
|     let mut derives = vec![quote! { Debug }]; | ||||
|     let mut derive_deserialize = true; | ||||
| 
 | ||||
|     derives.extend( | ||||
|         input | ||||
|         ty_def | ||||
|             .attrs | ||||
|             .iter() | ||||
|             .filter(|attr| attr.path.is_ident("incoming_derive")) | ||||
| @ -52,87 +69,18 @@ pub fn expand_derive_incoming(input: DeriveInput) -> syn::Result<TokenStream> { | ||||
|         quote! { #ruma_common::serde::_FakeDeriveSerde } | ||||
|     }); | ||||
| 
 | ||||
|     let input_attrs = | ||||
|         input.attrs.iter().filter(|attr| filter_input_attrs(attr)).collect::<Vec<_>>(); | ||||
|     ty_def.attrs.retain(filter_input_attrs); | ||||
|     clean_generics(&mut ty_def.generics); | ||||
| 
 | ||||
|     let data = match input.data.clone() { | ||||
|         Data::Union(_) => panic!("#[derive(Incoming)] does not support Union types"), | ||||
|         Data::Enum(e) => DataKind::Enum(e.variants.into_iter().collect()), | ||||
|         Data::Struct(s) => match s.fields { | ||||
|             Fields::Named(fs) => { | ||||
|                 DataKind::Struct(fs.named.into_iter().collect(), StructKind::Struct) | ||||
|             } | ||||
|             Fields::Unnamed(fs) => { | ||||
|                 DataKind::Struct(fs.unnamed.into_iter().collect(), StructKind::Tuple) | ||||
|             } | ||||
|             Fields::Unit => DataKind::Unit, | ||||
|         }, | ||||
|     }; | ||||
| 
 | ||||
|     match data { | ||||
|         DataKind::Unit => Ok(TokenStream::new()), | ||||
|         DataKind::Enum(mut vars) => { | ||||
|             let mut found_lifetime = false; | ||||
|             for var in &mut vars { | ||||
|                 for field in &mut var.fields { | ||||
|                     if strip_lifetimes(&mut field.ty) { | ||||
|                         found_lifetime = true; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if !found_lifetime { | ||||
|                 return Ok(TokenStream::new()); | ||||
|             } | ||||
| 
 | ||||
|             let vis = input.vis; | ||||
|             let doc = format!("'Incoming' variant of [{ty}](enum.{ty}.html).", ty = &input.ident); | ||||
|             let incoming_ident = format_ident!("Incoming{}", input.ident, span = Span::call_site()); | ||||
|             let mut gen_copy = input.generics.clone(); | ||||
|             let (_, ty_gen) = split_for_impl_lifetime_less(&mut gen_copy); | ||||
|     let doc = format!("'Incoming' variant of [{}].", &ty_def.ident); | ||||
|     ty_def.ident = format_ident!("Incoming{}", ty_def.ident, span = Span::call_site()); | ||||
| 
 | ||||
|     Ok(quote! { | ||||
|         #[doc = #doc] | ||||
|         #[derive( #( #derives ),* )] | ||||
|                 #( #input_attrs )* | ||||
|                 #vis enum #incoming_ident #ty_gen { #( #vars, )* } | ||||
|         #ty_def | ||||
|     }) | ||||
| } | ||||
|         DataKind::Struct(mut fields, struct_kind) => { | ||||
|             let mut found_lifetime = false; | ||||
|             for field in &mut fields { | ||||
|                 if !matches!(field.vis, syn::Visibility::Public(_)) { | ||||
|                     return Err(syn::Error::new_spanned(field, "All fields must be marked `pub`")); | ||||
|                 } | ||||
|                 if strip_lifetimes(&mut field.ty) { | ||||
|                     found_lifetime = true; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if !found_lifetime { | ||||
|                 return Ok(TokenStream::new()); | ||||
|             } | ||||
| 
 | ||||
|             let vis = input.vis; | ||||
|             let doc = format!("'Incoming' variant of [{ty}](struct.{ty}.html).", ty = &input.ident); | ||||
|             let incoming_ident = format_ident!("Incoming{}", input.ident, span = Span::call_site()); | ||||
|             let mut gen_copy = input.generics.clone(); | ||||
|             let (_, ty_gen) = split_for_impl_lifetime_less(&mut gen_copy); | ||||
| 
 | ||||
|             let struct_def = match struct_kind { | ||||
|                 StructKind::Struct => quote! { { #(#fields,)* } }, | ||||
|                 StructKind::Tuple => quote! { ( #(#fields,)* ); }, | ||||
|             }; | ||||
| 
 | ||||
|             Ok(quote! { | ||||
|                 #[doc = #doc] | ||||
|                 #[derive( #( #derives ),* )] | ||||
|                 #( #input_attrs )* | ||||
|                 #vis struct #incoming_ident #ty_gen #struct_def | ||||
|             }) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Keep any `cfg`, `cfg_attr`, `serde` or `non_exhaustive` attributes found and pass them to the
 | ||||
| /// Incoming variant.
 | ||||
| @ -144,16 +92,13 @@ fn filter_input_attrs(attr: &Attribute) -> bool { | ||||
|         || attr.path.is_ident("allow") | ||||
| } | ||||
| 
 | ||||
| fn split_for_impl_lifetime_less(generics: &mut Generics) -> (ImplGenerics<'_>, TypeGenerics<'_>) { | ||||
| fn clean_generics(generics: &mut Generics) { | ||||
|     generics.params = generics | ||||
|         .params | ||||
|         .clone() | ||||
|         .into_iter() | ||||
|         .filter(|param| !matches!(param, GenericParam::Lifetime(_))) | ||||
|         .collect(); | ||||
| 
 | ||||
|     let (impl_gen, ty_gen, _) = generics.split_for_impl(); | ||||
|     (impl_gen, ty_gen) | ||||
| } | ||||
| 
 | ||||
| fn strip_lifetimes(field_type: &mut Type) -> bool { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user