1#![feature(proc_macro_diagnostic)]
2#![feature(const_trait_impl)]
3#![feature(decl_macro)]
4
5use proc_macro::{TokenStream};
6use quote::{format_ident, ToTokens};
7use syn::{GenericParam, ItemStruct, GenericArgument, Member, Index, ItemImpl};
8use quote::{quote};
9use syn::punctuated::Punctuated;
10use syn::token::{Comma};
11
12#[proc_macro_attribute]
83pub fn dst(_attr:TokenStream, input:TokenStream) -> TokenStream{
84    let item_struct:ItemStruct = syn::parse(input.clone()).unwrap();
85    let struct_name = item_struct.ident.clone();
86    let mut struct_generics_param = item_struct.generics.params.clone();
87    let struct_where_clause = item_struct.generics.where_clause.clone();
88    let dst_type = item_struct.fields.iter().last().unwrap().ty.clone();
89    let field_num = item_struct.fields.iter().len();
90    let dst_field:Member = item_struct.fields.iter().last().unwrap().ident
91        .clone().map_or(Member::Unnamed(Index::from(field_num)),|i|{
92        Member::Named(i)
93    });
94
95    if !struct_generics_param.trailing_punct()&&!struct_generics_param.is_empty(){
96        struct_generics_param.push_punct(Comma::default());
97    }
98
99    let struct_generics_arg = struct_generics_param
100        .iter()
101        .fold(Punctuated::<GenericArgument, Comma>::new(),|mut last,i|{
102            let push = match i.clone(){
103                GenericParam::Type(t)=>{
104                    let t = t.ident;
105                    GenericArgument::Type(syn::parse(quote!(#t).into()).unwrap())
106                },
107                GenericParam::Const(t)=>{
108                    let t = t.ident;
109                    GenericArgument::Const(syn::parse(quote!(#t).into()).unwrap())
110                },
111                GenericParam::Lifetime(t)=>GenericArgument::Lifetime(t.lifetime),
112            };
113            last.push(push);
114            last.push_punct(Comma::default());
115            last
116        });
117
118    let mut new_struct = quote!(#[repr(C)]);
119    new_struct.extend(item_struct.into_token_stream());
120    let new_struct:ItemStruct = syn::parse(new_struct.into()).unwrap();
121
122    let mut fst_struct = new_struct.clone();
123    let fst_ident = format_ident!("{}Fst",struct_name);
124    fst_struct.ident = fst_ident.clone();
125    fst_struct.fields.iter_mut()
126        .last().unwrap().ty = syn::parse(quote!(core::marker::PhantomData< #dst_type >).into()).unwrap();
127
128    let mut init_struct = new_struct.clone();
129    let init_ident = format_ident!("{}Init",struct_name.to_string());
130    init_struct.ident = init_ident.clone();
131    init_struct.generics.params
132        .push(GenericParam::Type(syn::parse(quote!(INIT:dst_init::EmplaceInitializer<Output=#dst_type>).into()).unwrap()));
133    init_struct.fields.iter_mut()
134        .last().unwrap().ty = syn::parse(quote!(INIT).into()).unwrap();
135
136    let init_ident = init_struct.ident.clone();
137    let impl_emplace:ItemImpl = syn::parse(quote!(
138        impl<#struct_generics_param INIT:dst_init::EmplaceInitializer<Output=#dst_type>> dst_init::EmplaceInitializer for #init_ident<#struct_generics_arg INIT>
139            #struct_where_clause
140        {
141            type Output = #struct_name<#struct_generics_arg>;
142            #[inline(always)]
143            fn layout(&mut self) -> core::alloc::Layout{
144                use core::alloc::Layout;
145                let layout = Layout::new::<#fst_ident<#struct_generics_arg>>();
146                layout
147                    .extend(self.#dst_field.layout())
148                    .unwrap()
149                    .0
150                    .pad_to_align()
151            }
152
153            #[inline(always)]
154            fn emplace(mut self, ptr: core::ptr::NonNull<u8>) -> core::ptr::NonNull<Self::Output>{unsafe{
155                use core::ptr;
156                use core::ptr::NonNull;
157                use core::alloc::Layout;
158                use core::mem;
159                use dst_init::EmplaceInitializer;
160
161                let fst_layout = Layout::new::<#fst_ident<#struct_generics_arg>>();
162                let dst_layout = self.#dst_field.layout();
163                let dst = ptr
164                    .as_ptr()
165                    .add(mem::size_of::<#fst_ident<#struct_generics_arg>>())
166                    .add(fst_layout.padding_needed_for(dst_layout.align()));
167                let fst = ptr::read(&self as *const Self as *const _);
168                let dst_init = ptr::read(&self.#dst_field as *const INIT);
169                mem::forget(self);
170                ptr.as_ptr().cast::<#fst_ident<#struct_generics_arg>>().write(fst);
171                let (_, meta) = dst_init
172                    .emplace(NonNull::new(dst.cast()).unwrap())
173                    .to_raw_parts();
174                mem::transmute(NonNull::<#dst_type>::from_raw_parts(ptr, meta))
175            }}
176        }
177    ).into()).unwrap();
178
179    let impl_init:ItemImpl = syn::parse(quote!(
180        impl<#struct_generics_param DstInit:dst_init::EmplaceInitializer<Output=#dst_type>> dst_init::Initializer<DstInit> for #struct_name<#struct_generics_arg>
181            #struct_where_clause
182        {
183            type Init = #init_ident<#struct_generics_arg DstInit>;
184        }
185    ).into()).unwrap();
186
187    let mut output = new_struct.into_token_stream();
188    output.extend(fst_struct.into_token_stream());
189    output.extend(init_struct.into_token_stream());
190    output.extend(impl_emplace.into_token_stream());
191    output.extend(impl_init.into_token_stream());
192
193    output.into()
194}