1use std::{borrow::Cow, collections::HashMap, marker::PhantomData, path::PathBuf, rc::Rc};
2
3use proc_macro2::Ident;
4
5#[cfg(feature = "derive")]
6pub use structdump_derive::Codegen;
7
8#[doc(hidden)]
10pub use proc_macro2::TokenStream;
11#[doc(hidden)]
12pub use quote::{format_ident, quote};
13
14pub trait Codegen {
15 fn gen_code(&self, res: &mut CodegenResult, unique: bool) -> TokenStream;
16}
17impl<'a, T> Codegen for &'a T
18where
19 T: Codegen,
20{
21 fn gen_code(&self, res: &mut CodegenResult, unique: bool) -> TokenStream {
22 (*self).gen_code(res, unique)
23 }
24}
25
26#[derive(Default)]
27pub struct CodegenResult {
28 snippets: Vec<TokenStream>,
29 codes: HashMap<String, usize>,
30 id: usize,
31}
32impl CodegenResult {
33 pub fn add_code(
34 &mut self,
35 code: TokenStream,
36 ty: Option<TokenStream>,
37 unique: bool,
38 ) -> TokenStream {
39 if unique {
40 return code;
41 }
42 let code_str = code.to_string();
43 #[allow(clippy::map_entry)]
44 let var_name = if !self.codes.contains_key(&code_str) {
45 let value = self.id;
46 self.id += 1;
47 self.codes.insert(code_str, value);
48 let var_name = format_ident!("code_{:x}", value);
49 let ty = ty.map(|t| quote! {: #t});
50 self.snippets.push(quote! {
51 let #var_name #ty = #code;
52 });
53 var_name
54 } else {
55 let value = self.codes.get(&code_str).unwrap();
56 format_ident!("code_{:x}", value)
57 };
58 quote! {#var_name.clone()}
59 }
60 pub fn add_value(&mut self, s: impl Codegen, unique: bool) -> TokenStream {
61 s.gen_code(self, unique)
62 }
63 pub fn codegen(&mut self, s: &impl Codegen, unique: bool) -> TokenStream {
64 let fin_val = s.gen_code(self, unique);
65 let snippets = self.snippets.iter();
66 quote! {{
67 #(#snippets)*
68 #fin_val
69 }}
70 }
71}
72
73impl<T: Codegen> Codegen for Option<T> {
74 fn gen_code(&self, res: &mut CodegenResult, unique: bool) -> TokenStream {
75 if let Some(val) = self {
76 let val = res.add_value(val, unique);
77 quote! {
78 structdump_import::Option::Some(#val)
79 }
80 } else {
81 quote!(structdump_import::Option::None)
82 }
83 }
84}
85impl Codegen for Rc<str> {
86 fn gen_code(&self, res: &mut CodegenResult, _unique: bool) -> TokenStream {
87 let s: &str = self;
88 res.add_code(
89 quote! {
90 <structdump_import::Rc<str>>::from(#s)
91 },
92 Some(quote![structdump_import::Rc<str>]),
93 false,
94 )
95 }
96}
97impl<T: Codegen> Codegen for Rc<T> {
98 fn gen_code(&self, res: &mut CodegenResult, _unique: bool) -> TokenStream {
99 let v: &T = self;
100 let v = res.add_value(v, true);
101 res.add_code(
102 quote! {
103 structdump_import::Rc::new(#v)
104 },
105 Some(quote![structdump_import::Rc<_>]),
106 false,
107 )
108 }
109}
110impl Codegen for Cow<'_, str> {
111 fn gen_code(&self, res: &mut CodegenResult, _unique: bool) -> TokenStream {
112 let v: &str = self;
113 let v = res.add_value(v, true);
114 quote! {structdump_import::Cow::Borrowed(#v)}
115 }
116}
117impl<T: Codegen> Codegen for Vec<T> {
118 fn gen_code(&self, res: &mut CodegenResult, unique: bool) -> TokenStream {
119 let value = self
120 .iter()
121 .map(|v| res.add_value(v, unique))
122 .collect::<Vec<_>>();
123 res.add_code(
124 quote! {
125 structdump_import::vec![
126 #(#value),*
127 ]
128 },
129 Some(quote![structdump_import::Vec<_>]),
130 unique,
131 )
132 }
133}
134impl Codegen for &str {
135 fn gen_code(&self, res: &mut CodegenResult, _unique: bool) -> TokenStream {
136 let v: &str = self;
137 res.add_code(quote! {#v}, Some(quote![&'static str]), true)
139 }
140}
141impl Codegen for bool {
142 fn gen_code(&self, _res: &mut CodegenResult, _unique: bool) -> TokenStream {
143 quote! {#self}
144 }
145}
146
147impl Codegen for String {
148 fn gen_code(&self, res: &mut CodegenResult, unique: bool) -> TokenStream {
149 let v = res.add_value(self as &str, true);
150 res.add_code(
151 quote! {#v.to_owned()},
152 Some(quote! {structdump_import::String}),
153 unique,
154 )
155 }
156}
157impl Codegen for PathBuf {
158 fn gen_code(&self, _res: &mut CodegenResult, _unique: bool) -> TokenStream {
159 quote! {
160 panic!("pathbuf is not supported")
161 }
162 }
163}
164
165macro_rules! num_impl {
166 ($($t:ty)+) => {$(
167 impl Codegen for $t {
168 fn gen_code(&self, _res: &mut CodegenResult, _unique: bool) -> TokenStream {
169 quote!{#self}
170 }
171 }
172 )+};
173}
174num_impl!(u8 u16 u32 u64 u128 i8 i16 i32 i64 i128 isize usize f32 f64);
175
176macro_rules! impl_tuple {
177 ($($gen:ident)*) => {
178 #[allow(non_snake_case)]
179 impl<$($gen,)*> Codegen for ($($gen,)*)
180 where
181 $($gen: Codegen,)*
182 {
183 fn gen_code(&self, res: &mut CodegenResult, unique: bool) -> TokenStream {
184 let ($($gen,)*) = &self;
185 let values: Vec<TokenStream> = vec![
186 $({
187 res.add_value($gen, unique)
188 },)*
189 ];
190 res.add_code(
191 quote! {
192 (#(#values,)*)
193 },
194 None,
195 unique,
196 )
197 }
198 }
199 };
200 ($($cur:ident)* @ $c:ident $($rest:ident)*) => {
201 impl_tuple!($($cur)*);
202 impl_tuple!($($cur)* $c @ $($rest)*);
203 };
204 ($($cur:ident)* @) => {
205 impl_tuple!($($cur)*);
206 }
207}
208impl_tuple! {
209 @ A B C D E F G H I J K L
210}
211
212mod sealed {
213 pub(super) trait StructType {}
214 impl StructType for super::Named {}
215 impl StructType for super::Unnamed {}
216 impl StructType for super::Unit {}
217}
218
219pub struct Named;
220pub struct Unnamed;
221pub struct Unit;
222pub struct StructBuilder<Type> {
223 name: Ident,
224 variant: Option<TokenStream>,
225 fields: Vec<TokenStream>,
226 unique: bool,
227 _marker: PhantomData<Type>,
228}
229impl<Type> StructBuilder<Type> {
230 pub fn new(name: Ident, variant: Option<Ident>, unique: bool) -> Self {
231 Self {
232 name,
233 variant: variant.map(|i| quote! {::#i}),
234 fields: vec![],
235 unique,
236 _marker: PhantomData,
237 }
238 }
239}
240
241impl StructBuilder<Named> {
242 #[inline]
243 pub fn field(mut self, res: &mut CodegenResult, name: Ident, value: &impl Codegen) -> Self {
244 let val = res.add_value(value, self.unique);
245 self.fields.push(quote!(#name: #val,));
246 self
247 }
248 pub fn build(self, res: &mut CodegenResult) -> TokenStream {
249 let name = &self.name;
250 let variant = &self.variant;
251 let fields = &self.fields;
252 res.add_code(
253 quote! {structdump_import::#name #variant {
254 #(#fields)*
255 }},
256 Some(quote! {structdump_import::#name}),
257 self.unique,
258 )
259 }
260}
261
262impl StructBuilder<Unnamed> {
263 #[inline]
264 pub fn field(mut self, res: &mut CodegenResult, value: &impl Codegen) -> Self {
265 let val = res.add_value(value, self.unique);
266 self.fields.push(quote!(#val,));
267 self
268 }
269 pub fn build(self, res: &mut CodegenResult) -> TokenStream {
270 let name = &self.name;
271 let variant = &self.variant;
272 let fields = &self.fields;
273 res.add_code(
274 quote! {structdump_import::#name #variant(
275 #(#fields)*
276 )},
277 Some(quote! {structdump_import::#name}),
278 self.unique,
279 )
280 }
281}
282
283impl StructBuilder<Unit> {
284 pub fn build(self) -> TokenStream {
285 let name = &self.name;
286 let variant = &self.variant;
287 quote! {structdump_import::#name #variant}
288 }
289}