A better Rust ATProto crate
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}