A better Rust ATProto crate
at main 136 lines 4.5 kB view raw
1//! Implementation of #[derive(IntoStatic)] macro 2 3use proc_macro2::TokenStream; 4use quote::quote; 5use syn::{parse2, Data, DeriveInput, Fields, GenericParam}; 6 7/// Implementation for the IntoStatic derive macro 8pub fn impl_derive_into_static(input: TokenStream) -> TokenStream { 9 let input = match parse2::<DeriveInput>(input) { 10 Ok(input) => input, 11 Err(e) => return e.to_compile_error(), 12 }; 13 14 let name = &input.ident; 15 let generics = &input.generics; 16 17 // Build impl generics and where clause 18 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); 19 20 // Build the Output type with all lifetimes replaced by 'static 21 let output_generics = generics.params.iter().map(|param| match param { 22 GenericParam::Lifetime(_) => quote! { 'static }, 23 GenericParam::Type(ty) => { 24 let ident = &ty.ident; 25 quote! { #ident } 26 } 27 GenericParam::Const(c) => { 28 let ident = &c.ident; 29 quote! { #ident } 30 } 31 }); 32 33 let output_type = if generics.params.is_empty() { 34 quote! { #name } 35 } else { 36 quote! { #name<#(#output_generics),*> } 37 }; 38 39 // Generate the conversion body based on struct/enum 40 let conversion = match &input.data { 41 Data::Struct(data_struct) => generate_struct_conversion(name, &data_struct.fields), 42 Data::Enum(data_enum) => generate_enum_conversion(name, data_enum), 43 Data::Union(_) => { 44 return syn::Error::new_spanned(input, "IntoStatic cannot be derived for unions") 45 .to_compile_error(); 46 } 47 }; 48 49 quote! { 50 impl #impl_generics ::jacquard_common::IntoStatic for #name #ty_generics #where_clause { 51 type Output = #output_type; 52 53 fn into_static(self) -> Self::Output { 54 #conversion 55 } 56 } 57 } 58} 59 60fn generate_struct_conversion(name: &syn::Ident, fields: &Fields) -> TokenStream { 61 match fields { 62 Fields::Named(fields) => { 63 let field_conversions = fields.named.iter().map(|f| { 64 let field_name = &f.ident; 65 quote! { #field_name: self.#field_name.into_static() } 66 }); 67 quote! { 68 #name { 69 #(#field_conversions),* 70 } 71 } 72 } 73 Fields::Unnamed(fields) => { 74 let field_conversions = fields.unnamed.iter().enumerate().map(|(i, _)| { 75 let index = syn::Index::from(i); 76 quote! { self.#index.into_static() } 77 }); 78 quote! { 79 #name(#(#field_conversions),*) 80 } 81 } 82 Fields::Unit => { 83 quote! { #name } 84 } 85 } 86} 87 88fn generate_enum_conversion( 89 name: &syn::Ident, 90 data_enum: &syn::DataEnum, 91) -> TokenStream { 92 let variants = data_enum.variants.iter().map(|variant| { 93 let variant_name = &variant.ident; 94 match &variant.fields { 95 Fields::Named(fields) => { 96 let field_names: Vec<_> = fields.named.iter().map(|f| &f.ident).collect(); 97 let field_conversions = field_names.iter().map(|field_name| { 98 quote! { #field_name: #field_name.into_static() } 99 }); 100 quote! { 101 #name::#variant_name { #(#field_names),* } => { 102 #name::#variant_name { 103 #(#field_conversions),* 104 } 105 } 106 } 107 } 108 Fields::Unnamed(fields) => { 109 let field_bindings: Vec<_> = (0..fields.unnamed.len()) 110 .map(|i| { 111 syn::Ident::new(&format!("field_{}", i), proc_macro2::Span::call_site()) 112 }) 113 .collect(); 114 let field_conversions = field_bindings.iter().map(|binding| { 115 quote! { #binding.into_static() } 116 }); 117 quote! { 118 #name::#variant_name(#(#field_bindings),*) => { 119 #name::#variant_name(#(#field_conversions),*) 120 } 121 } 122 } 123 Fields::Unit => { 124 quote! { 125 #name::#variant_name => #name::#variant_name 126 } 127 } 128 } 129 }); 130 131 quote! { 132 match self { 133 #(#variants),* 134 } 135 } 136}