structdump/
lib.rs

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// Hidden because only used by proc macros
9#[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		// Strings are deduplicated, but lets keep output code smaller
138		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}