stable_type_layout_derive/
lib.rs1use proc_macro::TokenStream;
7use proc_macro2::TokenStream as TokenStream2;
8use quote::quote;
9use syn::{Data, DeriveInput, Fields, Ident, Index, parse_macro_input};
10
11#[proc_macro_derive(TypeLayout)]
20pub fn derive_type_layout(input: TokenStream) -> TokenStream {
21 let ast = parse_macro_input!(input as DeriveInput);
22 let ident = &ast.ident;
23 let name = ident.to_string();
24 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
25 let structure = match &ast.data {
26 Data::Struct(data) => {
27 let fields = fields(&data.fields);
28 quote! { ::stable_type_layout::TypeStructure::Struct { fields: &[#(#fields),*] } }
29 }
30 Data::Union(data) => {
31 let fields = data.fields.named.iter().map(|f| named_field(f.ident.as_ref().unwrap()));
32 quote! { ::stable_type_layout::TypeStructure::Union { fields: &[#(#fields),*] } }
33 }
34 Data::Enum(data) => {
35 let fieldless = data.variants.iter().all(|v| matches!(v.fields, Fields::Unit));
38 let variants = data.variants.iter().enumerate().map(|(index, variant)| {
39 let variant_ident = &variant.ident;
40 let variant_name = variant_ident.to_string();
41 let discriminant = if fieldless {
42 quote! { ::core::option::Option::Some(Self::#variant_ident as i64) }
43 } else {
44 quote! { ::core::option::Option::None }
45 };
46 quote! {
47 ::stable_type_layout::Variant {
48 name: #variant_name,
49 index: #index,
50 discriminant: #discriminant,
51 }
52 }
53 });
54 quote! { ::stable_type_layout::TypeStructure::Enum { variants: &[#(#variants),*] } }
55 }
56 };
57 quote! {
58 impl #impl_generics ::stable_type_layout::TypeLayout for #ident #ty_generics #where_clause {
59 const TYPE_LAYOUT: ::stable_type_layout::TypeLayoutInfo = ::stable_type_layout::TypeLayoutInfo {
60 name: #name,
61 size: ::core::mem::size_of::<Self>(),
62 align: ::core::mem::align_of::<Self>(),
63 structure: #structure,
64 };
65 }
66 }
67 .into()
68}
69
70fn fields(fields: &Fields) -> Vec<TokenStream2> {
71 match fields {
72 Fields::Named(named) => named.named.iter().map(|f| named_field(f.ident.as_ref().unwrap())).collect(),
73 Fields::Unnamed(unnamed) => (0..unnamed.unnamed.len())
74 .map(|i| {
75 let index = Index::from(i);
76 let name = i.to_string();
77 quote! { ::stable_type_layout::Field { name: #name, offset: ::core::mem::offset_of!(Self, #index) } }
78 })
79 .collect(),
80 Fields::Unit => vec![],
81 }
82}
83
84fn named_field(ident: &Ident) -> TokenStream2 {
85 let name = ident.to_string();
86 quote! { ::stable_type_layout::Field { name: #name, offset: ::core::mem::offset_of!(Self, #ident) } }
87}