dst_init_macros/
lib.rs

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/// # Usage:
13/// Add `#[dst]` ahead of struct item as below:
14/// ```rust
15/// #[dst]
16/// struct Foo{
17///     a:u8,
18///     b:[usize],
19/// }
20/// ```
21/// after expansion:
22/// ```rust
23/// use std::marker::PhantomData;
24/// #[repr(C)]
25/// struct Foo{
26///     a:u8,
27///     b:[usize],
28/// }
29///
30/// #[repr(C)]
31/// struct FooInit<INIT:EmplaceInitializer<Output=[usize]>>{
32///     a:u8,
33///     b:INIT,
34/// }
35///
36/// #[repr(C)]
37/// struct FooFst{
38///     a:u8,
39///     b:PhantomData<[usize]>,
40/// }
41/// ```
42/// You can also use it in nestly. With above Foo:
43/// ```rust
44/// #[dst]
45/// struct Bar{
46///     c:usize,
47///     d:Foo
48/// }
49/// ```
50/// For bar there will be 3 structs `Bar`,`BarInit`,`BarFst` after expansion.
51/// The `BarInit` looks like this:
52/// ```rust
53/// #[repr(C)]
54/// struct BarInit<INIT:EmplaceInitializer<Output=Foo>>{
55///     c:usize,
56///     d:INIT,
57/// }
58/// ```
59///
60/// # Use Case:
61/// - 1 add simpler api
62///
63///   we usually provide a function to create the initializer
64/// ```rust
65/// #[dst]
66/// struct SomePacket{
67///     src:u32,
68///     dst:u32,
69///     options:[u8],
70/// }
71/// impl SomePacket{
72///     fn initializer<Init:EmplaceInitializer<Output=[u8]>>(src:u32,dst:u32,init:Init)->SomePacketInit<Init>{
73///         SomePacketInit{
74///             src,
75///             dst,
76///             options:init
77///         }
78///     }
79/// }
80/// ```
81///
82#[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}