use proc_macro2::Span;
use quote::quote;
use syn::{Ident, Lifetime};
pub fn derive_tostatic(s: synstructure::Structure) -> proc_macro2::TokenStream {
let ast = s.ast();
let debug_derive = ast.attrs.iter().any(|attr| {
attr.meta
.path()
.is_ident(&Ident::new("debug_derive", Span::call_site()))
});
// if deriving a struct, there will be only one variant
// for enums, this will iterate on each variant
let body = s.each_variant(|vi| {
// bindings can be empty for unit variants
let instrs = vi
.bindings()
.iter()
.enumerate()
.fold(quote! {}, |acc, (idx, bi)| {
let ident = Ident::new(&format!("_{idx}"), Span::call_site());
quote! { #acc let #ident = #bi.to_static(); }
});
// use construct() to handle possible cases (unit/named/unnamed)
let c = vi.construct(|_f, i| {
let ident = Ident::new(&format!("_{i}"), Span::call_site());
quote! { #ident }
});
quote! { #instrs #c }
});
let struct_ident = &ast.ident;
// check if struct has lifetimes
let static_token = match ast.generics.lifetimes().count() {
0 => None,
1 => {
let lt = Lifetime::new("'static", Span::call_site());
Some(quote! {<#lt>})
}
_ => {
let lt_static = Lifetime::new("'static", Span::call_site());
let lts = ast.generics.lifetimes().map(|_| lt_static.clone());
Some(quote! {<#(#lts),*>})
}
};
let ts = s.gen_impl(quote! {
gen impl asn1_rs::ToStatic for @Self {
type Owned = #struct_ident #static_token;
fn to_static(&self) -> Self::Owned {
match *self {
#body
}
}
}
});
if debug_derive {
eprintln!("TS: {ts}");
}
ts
}