dst_container_derive/
lib.rs1use proc_macro::TokenStream;
2use proc_macro2::Ident;
3use proc_macro_crate::{crate_name, FoundCrate};
4use quote::quote;
5use syn::{
6 parse::{Parse, Parser},
7 parse_str, Attribute, Data, DeriveInput, Field, Fields, GenericParam, Generics, Type,
8 Visibility,
9};
10
11struct PreDerive {
12 attrs: Vec<Attribute>,
13 vis: Visibility,
14 struct_name: Ident,
15 generics: Generics,
16 data: Data,
17 generic_inputs: proc_macro2::TokenStream,
18 dst_crate_name: proc_macro2::TokenStream,
19}
20
21fn pre_derive(input: TokenStream) -> PreDerive {
22 let struct_input: DeriveInput = syn::parse(input).unwrap();
23 let generics = struct_input.generics;
24 let generic_inputs = generics
25 .params
26 .iter()
27 .map(|p| match p {
28 GenericParam::Type(p) => {
29 let ident = &p.ident;
30 quote!(#ident)
31 }
32 GenericParam::Lifetime(p) => {
33 let life = &p.lifetime;
34 quote!(#life)
35 }
36 GenericParam::Const(p) => {
37 let ident = &p.ident;
38 quote!(#ident)
39 }
40 })
41 .collect::<Vec<_>>();
42 let generic_inputs = if generic_inputs.is_empty() {
43 quote!()
44 } else {
45 quote!(<#(#generic_inputs,)*>)
46 };
47
48 let dst_crate_name = match crate_name("dst-container").unwrap() {
49 FoundCrate::Itself => quote!(crate),
50 FoundCrate::Name(name) => {
51 let name = parse_str::<Ident>(&name).unwrap();
52 quote!(::#name)
53 }
54 };
55
56 PreDerive {
57 attrs: struct_input.attrs,
58 vis: struct_input.vis,
59 struct_name: struct_input.ident,
60 generics,
61 data: struct_input.data,
62 generic_inputs,
63 dst_crate_name,
64 }
65}
66
67#[proc_macro_derive(MaybeUninitProject)]
68pub fn derive_maybe_uninit_project(input: TokenStream) -> TokenStream {
69 let PreDerive {
70 attrs,
71 vis,
72 struct_name,
73 generics,
74 data,
75 generic_inputs,
76 dst_crate_name,
77 } = pre_derive(input);
78
79 let project_struct_name = parse_str::<Ident>(&format!("__MaybeUninit{struct_name}")).unwrap();
80
81 let repr = attrs
82 .iter()
83 .find(|attr| {
84 attr.path()
85 .get_ident()
86 .map_or(false, |ident| ident == "repr")
87 })
88 .expect("Need #[repr(...)].");
89 let repr_content = repr.meta.require_list().unwrap().tokens.to_string();
90 if !matches!(repr_content.as_str(), "C" | "packed" | "transparent") {
91 panic!(
92 "Expected #[repr(C)], #[repr(packed)], #[repr(transparent)] only, get `{}`.",
93 repr_content
94 );
95 }
96
97 let project_data_declare = match data {
98 Data::Struct(data) => match data.fields {
99 Fields::Named(fields) => {
100 let fields = fields.named.into_iter().collect::<Vec<_>>();
101 let fields = map_maybe_uninit_fields(fields, &dst_crate_name);
102 quote!({#(#fields,)*})
103 }
104 Fields::Unnamed(fields) => {
105 let fields = fields.unnamed.into_iter().collect::<Vec<_>>();
106 let fields = map_maybe_uninit_fields(fields, &dst_crate_name);
107 quote!((#(#fields,)*);)
108 }
109 _ => unimplemented!(),
110 },
111 _ => unimplemented!(),
112 };
113
114 let output = quote! {
115 #[doc(hidden)]
116 #repr
117 #vis struct #project_struct_name #generics #project_data_declare
118
119 impl #generics #dst_crate_name :: MaybeUninitProject for #struct_name #generic_inputs {
120 type Target = #project_struct_name #generic_inputs;
121 }
122 };
123 TokenStream::from(output)
124}
125
126fn map_maybe_uninit_fields(
127 fields: impl IntoIterator<Item = Field>,
128 dst_crate_name: &proc_macro2::TokenStream,
129) -> Vec<Field> {
130 fields
131 .into_iter()
132 .map(|mut field| {
133 let ty = field.ty;
134 field.ty = Type::parse
135 .parse2(quote!(<#ty as #dst_crate_name :: MaybeUninitProject>::Target))
136 .unwrap();
137 field
138 })
139 .collect()
140}
141
142#[proc_macro_derive(UnsizedClone)]
143pub fn derive_unsized_clone(input: TokenStream) -> TokenStream {
144 let PreDerive {
145 attrs: _,
146 vis: _,
147 struct_name,
148 generics,
149 data,
150 generic_inputs,
151 dst_crate_name,
152 } = pre_derive(input);
153
154 let (types, idents) = match data {
155 Data::Struct(data) => match data.fields {
156 Fields::Named(fields) => {
157 let fields = fields.named.into_iter().collect::<Vec<_>>();
158 let types = fields
159 .iter()
160 .map(|field| &field.ty)
161 .cloned()
162 .collect::<Vec<_>>();
163 let idents = fields
164 .iter()
165 .map(|field| field.ident.as_ref().unwrap())
166 .cloned()
167 .collect::<Vec<_>>();
168 (types, idents)
169 }
170 Fields::Unnamed(fields) => {
171 let fields = fields.unnamed.into_iter().collect::<Vec<_>>();
172 let types = fields
173 .iter()
174 .map(|field| &field.ty)
175 .cloned()
176 .collect::<Vec<_>>();
177 let idents = (0..fields.len())
178 .map(|i| Ident::parse.parse_str(&i.to_string()).unwrap())
179 .collect::<Vec<_>>();
180 (types, idents)
181 }
182 _ => unimplemented!(),
183 },
184 _ => unimplemented!(),
185 };
186
187 let where_clause = if types.is_empty() {
188 quote!()
189 } else {
190 quote!(where #(#types: UnsizedClone,)*)
191 };
192 let clone_to_statement = idents
193 .into_iter()
194 .map(|ident| quote!(self.#ident.clone_to(&mut dest.#ident);))
195 .collect::<Vec<_>>();
196
197 let output = quote! {
198 impl #generics #dst_crate_name :: UnsizedClone for #struct_name #generic_inputs #where_clause {
199 fn clone_to(&self, dest: &mut Self::Target) {
200 #(#clone_to_statement)*
201 }
202 }
203 };
204 TokenStream::from(output)
205}