layout_macro/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use proc_macro::TokenStream;
4use proc_macro2::Ident;
5use proc_macro2::Literal;
6use proc_macro2::TokenStream as TokenStream2;
7use syn::TypeGenerics;
8use syn::{DeriveInput, DataStruct, DataEnum, DataUnion, FieldsNamed, FieldsUnnamed, punctuated::Punctuated};
9
10
11#[proc_macro_derive(Layout, attributes())]
12pub fn derive_layout(input: TokenStream) -> TokenStream {
13    let derive = syn::parse_macro_input!(input as DeriveInput);
14
15    let generics = &derive.generics;
16    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
17
18    let ident = &derive.ident;
19    let data = &derive.data;
20
21    let token_stream = match data {
22        syn::Data::Struct(data_struct) => {
23            parse_struct(&data_struct, &ident, &ty_generics)
24        },
25        syn::Data::Enum(data_enum) => {
26            parse_enum(data_enum.clone(), ident.clone())
27        },
28        syn::Data::Union(data_union) => {
29            parse_union(data_union.clone(), ident.clone())
30        },
31    };
32
33
34    quote::quote! {
35        impl #impl_generics ::layout_lib::Layout for #ident #ty_generics #where_clause {
36            fn get_layout() -> ::layout_lib::LayoutInfo {
37                #token_stream
38            }
39        }
40    }
41    .into()
42}
43
44
45fn parse_struct(data_struct: &DataStruct, ident: &Ident, ty_generics: &TypeGenerics) -> TokenStream2 {
46    let unit = Punctuated::new();
47    let punctuated = match &data_struct.fields {
48        syn::Fields::Named(FieldsNamed{named, ..}) => {
49            named
50        },
51        syn::Fields::Unnamed(FieldsUnnamed{unnamed, ..}) => {
52            unnamed
53        },
54        syn::Fields::Unit => { &unit },
55    };
56
57    let mut struct_tokens = quote::quote! {
58        let mut struct_layout = ::layout_lib::LayoutInfo::new(
59            std::any::type_name::<#ident #ty_generics>(),
60            // std::any::TypeId::of::<#ident #ty_generics>(),
61            std::mem::size_of::<#ident #ty_generics>(),
62            std::mem::align_of::<#ident #ty_generics>(),
63            Vec::new(),
64        );
65    };
66
67    let mut i = 0;
68    for field in punctuated.iter() {
69        let field_ident = &field.ident;
70        let (field_ident, field_name) = match field_ident {
71            Some(ident) => (quote::quote!{ #ident }, Literal::string(&ident.to_string())),
72            None => {
73                let num = Literal::i32_unsuffixed(i);
74                (quote::quote!{ #num }, Literal::string(&i.to_string()))
75            },
76        };
77
78        i += 1;
79
80        let field_ty = &field.ty;
81        let field_tokens = quote::quote! {
82            let layout = ::layout_lib::LayoutInfo::new(
83                std::any::type_name::<#field_ty>(),
84                // std::any::TypeId::of::<#field_ty>(),
85                std::mem::size_of::<#field_ty>(),
86                std::mem::align_of::<#field_ty>(),
87                Vec::new(),
88            );
89
90            let field = ::layout_lib::Field {
91                name: #field_name,
92                offset: ::layout_lib::offset_of_struct!(#ident #ty_generics, #field_ident),
93                layout,
94            };
95
96            struct_layout.fields.push(field);
97        };
98        struct_tokens.extend(field_tokens.into_iter());
99    }
100
101    struct_tokens.extend(quote::quote! {
102        struct_layout.fields.sort_by_key(|v| v.offset);
103        struct_layout
104    }.into_iter());
105    struct_tokens
106}
107
108fn parse_enum(_data_enum: DataEnum, ident: Ident) -> TokenStream2 {
109    let mut struct_tokens = quote::quote! {
110        let mut struct_layout = ::layout_lib::LayoutInfo::new(
111            std::any::type_name::<#ident>(),
112            // std::any::TypeId::of::<#ident>(),
113            std::mem::size_of::<#ident>(),
114            std::mem::align_of::<#ident>(),
115            Vec::new(),
116        );
117    };
118
119    struct_tokens.extend(quote::quote! {
120        struct_layout
121    }.into_iter());
122    struct_tokens
123}
124
125fn parse_union(data_union: DataUnion, ident: Ident) -> TokenStream2 {
126    let mut struct_tokens = quote::quote! {
127        let mut struct_layout = ::layout_lib::LayoutInfo::new(
128            std::any::type_name::<#ident>(),
129            // std::any::TypeId::of::<#ident>(),
130            std::mem::size_of::<#ident>(),
131            std::mem::align_of::<#ident>(),
132            Vec::new(),
133        );
134    };
135
136    let mut i = 0;
137    for field in data_union.fields.named.into_iter() {
138        let field_ident = &field.ident;
139        let field_name = match field_ident{
140            Some(ident) => Literal::string(&ident.to_string()),
141            None => Literal::string(&i.to_string()),
142        };
143        i += 1;
144
145        let field_ty = &field.ty;
146        let field_tokens = quote::quote! {
147            let layout = ::layout_lib::LayoutInfo::new(
148                std::any::type_name::<#field_ty>(),
149                // std::any::TypeId::of::<#field_ty>(),
150                std::mem::size_of::<#field_ty>(),
151                std::mem::align_of::<#field_ty>(),
152                Vec::new(),
153            );
154
155            let field = ::layout_lib::Field {
156                name: #field_name,
157                offset: ::layout_lib::offset_of_struct!(#ident, #field_ident),
158                layout,
159            };
160
161            struct_layout.fields.push(field);
162        };
163        struct_tokens.extend(field_tokens.into_iter());
164    }
165
166    struct_tokens.extend(quote::quote! {
167        struct_layout
168    }.into_iter());
169    struct_tokens
170}