1#![doc = include_str!("../README.md")]
2
3use builder::impl_builder;
4use destr::impl_destr;
5use errs::{
6 panic_only_works_with_structs, panic_only_works_with_structs_with_named_fields,
7 panic_req_all_fields_same_generic, panic_req_single_generic,
8};
9use idents::{
10 array_len_ident, const_with_ident, field_idx_ident, ident_mut, set_ident, with_ident,
11};
12use proc_macro::TokenStream;
13use quote::quote;
14use syn::{
15 parse::{Parse, ParseStream},
16 parse_macro_input,
17 token::{Bracket, Paren, Semi},
18 Attribute, Data, DataStruct, DeriveInput, Expr, ExprPath, Field, Fields, FieldsNamed,
19 FieldsUnnamed, GenericParam, Ident, Type, TypeArray, TypePath, Visibility,
20};
21use utils::path_from_ident;
22
23use crate::{idents::assoc_field_idx_ident, trymap::impl_trymap};
24
25mod builder;
26mod destr;
27mod errs;
28mod idents;
29mod trymap;
30mod utils;
31
32const MACRO_NAME: &str = "generic_array_struct";
33
34#[repr(transparent)]
35struct GenericArrayStructParams(DeriveInput);
36
37impl GenericArrayStructParams {
39 #[inline]
40 pub fn struct_vis(&self) -> &Visibility {
41 &self.0.vis
42 }
43
44 #[inline]
45 pub fn struct_ident(&self) -> &Ident {
46 &self.0.ident
47 }
48
49 #[inline]
50 pub fn generic_ident(&self) -> &Ident {
51 let mut generic_iter = self.0.generics.params.iter();
52 let generic = match generic_iter.next() {
53 Some(GenericParam::Type(g)) => g,
54 _ => panic_req_single_generic(),
55 };
56 if generic_iter.next().is_some() {
57 panic_req_single_generic();
58 }
59 &generic.ident
60 }
61
62 #[inline]
63 pub fn data_struct(&self) -> &DataStruct {
64 match &self.0.data {
65 Data::Struct(ds) => ds,
66 _ => panic_only_works_with_structs(),
67 }
68 }
69
70 #[inline]
71 pub fn data_struct_mut(&mut self) -> &mut DataStruct {
72 match &mut self.0.data {
73 Data::Struct(ds) => ds,
74 _ => panic_only_works_with_structs(),
75 }
76 }
77
78 #[inline]
79 pub fn fields_named(&self) -> &FieldsNamed {
80 match &self.data_struct().fields {
81 Fields::Named(f) => f,
82 _ => panic_only_works_with_structs_with_named_fields(),
83 }
84 }
85
86 #[inline]
87 pub fn attrs(&self) -> &[Attribute] {
88 &self.0.attrs
89 }
90}
91
92struct AttrArgs {
93 array_field_vis: Visibility,
94 should_gen_builder: bool,
95 should_gen_destr: bool,
96 should_gen_trymap: bool,
97}
98
99impl Parse for AttrArgs {
100 fn parse(input: ParseStream) -> syn::Result<Self> {
101 let flags = [false; _];
102 let len = flags.len();
103 let [mut should_gen_builder, mut should_gen_destr, mut should_gen_trymap] = flags;
104
105 for _i in 0..len {
106 if !input.peek(Ident) {
107 break;
108 }
109 let id: Ident = input.parse()?;
110 if id == "builder" {
112 if should_gen_builder {
113 panic!("`builder` already set");
114 } else {
115 should_gen_builder = true;
116 }
117 } else if id == "destr" {
118 if should_gen_destr {
119 panic!("`destr` already set");
120 } else {
121 should_gen_destr = true;
122 }
123 } else if id == "trymap" {
124 if should_gen_trymap {
125 panic!("`trymap` already set");
126 } else {
127 should_gen_trymap = true;
128 }
129 } else {
130 panic!("Expected one of [`builder`, `destr`, `trymap`]")
131 }
132 }
133
134 if input.is_empty() {
135 return Ok(Self {
136 array_field_vis: Visibility::Inherited,
137 should_gen_builder,
138 should_gen_destr,
139 should_gen_trymap,
140 });
141 }
142
143 let array_field_vis = input.parse()?;
144 Ok(Self {
145 array_field_vis,
146 should_gen_builder,
147 should_gen_destr,
148 should_gen_trymap,
149 })
150 }
151}
152
153#[proc_macro_attribute]
155pub fn generic_array_struct(attr_arg: TokenStream, input: TokenStream) -> TokenStream {
156 let AttrArgs {
157 array_field_vis,
158 should_gen_builder,
159 should_gen_destr,
160 should_gen_trymap,
161 } = parse_macro_input!(attr_arg as AttrArgs);
162
163 let input = parse_macro_input!(input as DeriveInput);
164 let mut params = GenericArrayStructParams(input);
165
166 let mut fields_idx_consts = quote! {};
167 let mut fields_idx_assoc_consts = quote! {};
168 let mut accessor_mutator_impls = quote! {};
169 let mut const_with_impls = quote! {};
170 let n_fields =
171 params
172 .fields_named()
173 .named
174 .iter()
175 .enumerate()
176 .fold(0usize, |n_fields, (i, field)| {
177 let expect_same_generic = match &field.ty {
178 Type::Path(g) => g,
179 _ => panic_req_all_fields_same_generic(),
180 };
181 if !expect_same_generic
182 .path
183 .get_ident()
184 .map(|id| id == params.generic_ident())
185 .unwrap_or(false)
186 {
187 panic_req_all_fields_same_generic();
188 }
189
190 let field_vis = &field.vis;
191 let field_ident = field.ident.as_ref().unwrap();
193
194 let idx_ident = field_idx_ident(params.struct_ident(), field_ident);
196 fields_idx_consts.extend(quote! {
197 #field_vis const #idx_ident: usize = #i;
198 });
199
200 let assoc_idx_ident = assoc_field_idx_ident(field_ident);
203 fields_idx_assoc_consts.extend(quote! {
204 #field_vis const #assoc_idx_ident: usize = #i;
205 });
206
207 let id_mut = ident_mut(field_ident);
209 let set_id = set_ident(field_ident);
210 let with_id = with_ident(field_ident);
211 let field_attrs = &field.attrs;
213 accessor_mutator_impls.extend(quote! {
214 #(#field_attrs)*
215 #[inline]
216 #field_vis const fn #field_ident(&self) -> &T {
217 &self.0[#idx_ident]
218 }
219
220 #[inline]
221 #field_vis const fn #id_mut(&mut self) -> &mut T {
222 &mut self.0[#idx_ident]
223 }
224
225 #[inline]
227 #field_vis const fn #set_id(&mut self, val: T) -> T {
228 core::mem::replace(&mut self.0[#idx_ident], val)
229 }
230
231 #[inline]
232 #field_vis fn #with_id(mut self, val: T) -> Self {
233 self.0[#idx_ident] = val;
234 self
235 }
236 });
237
238 let const_with_id = const_with_ident(field_ident);
240 const_with_impls.extend(quote! {
241 #[inline]
242 #field_vis const fn #const_with_id(mut self, val: T) -> Self {
243 self.0[#idx_ident] = val;
244 self
245 }
246 });
247
248 n_fields + 1
249 });
250
251 let len_ident = array_len_ident(params.struct_ident());
252
253 let struct_vis = params.struct_vis();
254 let struct_ident = params.struct_ident();
255 let mut res = quote! {
256 #struct_vis const #len_ident: usize = #n_fields;
257
258 impl<T> #struct_ident<T> {
259 #accessor_mutator_impls
260 }
261
262 impl<T: Copy> #struct_ident<T> {
263 #const_with_impls
264 }
265
266 impl<T> #struct_ident<T> {
267 #struct_vis const LEN: usize = #n_fields;
268
269 #fields_idx_assoc_consts
270 }
271
272 #fields_idx_consts
273 };
274
275 if should_gen_builder {
276 res.extend(impl_builder(¶ms, struct_vis));
277 }
278
279 if should_gen_destr {
280 res.extend(impl_destr(¶ms, struct_vis));
281 }
282
283 if should_gen_trymap {
284 res.extend(impl_trymap(¶ms));
285 }
286
287 params.data_struct_mut().fields = Fields::Unnamed(FieldsUnnamed {
289 paren_token: Paren::default(),
290 unnamed: core::iter::once(Field {
291 vis: array_field_vis,
292 attrs: Vec::new(),
293 mutability: syn::FieldMutability::None,
294 ident: None,
295 colon_token: None,
296 ty: Type::Array(TypeArray {
297 bracket_token: Bracket::default(),
298 elem: Box::new(Type::Path(TypePath {
299 qself: None,
300 path: path_from_ident(params.generic_ident().clone()),
301 })),
302 semi_token: Semi::default(),
303 len: Expr::Path(ExprPath {
304 attrs: Vec::new(),
305 qself: None,
306 path: path_from_ident(len_ident),
307 }),
308 }),
309 })
310 .collect(),
311 });
312
313 let GenericArrayStructParams(input) = params;
315 res.extend(quote! { #input });
316
317 res.into()
318}