type_layout_derive/
lib.rs1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4
5use proc_macro2::{Ident, Literal};
6use quote::{quote, quote_spanned, ToTokens};
7use syn::{parse_macro_input, spanned::Spanned, Data, DeriveInput, Fields};
8
9#[proc_macro_derive(TypeLayout)]
10pub fn derive_type_layout(input: TokenStream) -> TokenStream {
11 let input = parse_macro_input!(input as DeriveInput);
13
14 let name = input.ident;
16 let name_str = Literal::string(&name.to_string());
17
18 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
19 let layout = layout_of_type(&name, &input.data);
20
21 let expanded = quote! {
23 impl #impl_generics ::type_layout::TypeLayout for #name #ty_generics #where_clause {
24 fn type_layout() -> ::type_layout::TypeLayoutInfo {
25 let mut last_field_end = 0;
26 let mut fields = Vec::new();
27
28 #layout
29
30 ::type_layout::TypeLayoutInfo {
31 name: ::std::borrow::Cow::Borrowed(#name_str),
32 size: std::mem::size_of::<#name>(),
33 alignment: ::std::mem::align_of::<#name>(),
34 fields,
35 }
36 }
37 }
38 };
39
40 TokenStream::from(expanded)
42}
43
44fn layout_of_type(struct_name: &Ident, data: &Data) -> proc_macro2::TokenStream {
45 match data {
46 Data::Struct(data) => match &data.fields {
47 Fields::Named(fields) => {
48 let values = fields.named.iter().map(|field| {
49 let field_name = field.ident.as_ref().unwrap();
50 let field_name_str = Literal::string(&field_name.to_string());
51 let field_ty = &field.ty;
52 let field_ty_str = Literal::string(&field_ty.to_token_stream().to_string());
53
54 quote_spanned! { field.span() =>
55 #[allow(unused_assignments)]
56 {
57 let size = ::std::mem::size_of::<#field_ty>();
58 let offset = ::type_layout::memoffset::offset_of!(#struct_name, #field_name);
59
60 if offset > last_field_end {
61 fields.push(::type_layout::Field::Padding {
62 size: offset - last_field_end
63 });
64 }
65
66 fields.push(::type_layout::Field::Field {
67 name: ::std::borrow::Cow::Borrowed(#field_name_str),
68 ty: ::std::borrow::Cow::Borrowed(#field_ty_str),
69 size,
70 });
71
72 last_field_end = offset + size;
73 }
74 }
75 });
76
77 quote! {
78 #(#values)*
79
80 let struct_size = ::std::mem::size_of::<#struct_name>();
81 if struct_size > last_field_end {
82 fields.push(::type_layout::Field::Padding {
83 size: struct_size - last_field_end,
84 });
85 }
86 }
87 }
88 Fields::Unnamed(_) => unimplemented!(),
89 Fields::Unit => unimplemented!(),
90 },
91 Data::Enum(_) | Data::Union(_) => unimplemented!("type-layout only supports structs"),
92 }
93}