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::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::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::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::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::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}