array_as_struct_derive/
lib.rs1use itertools::multiunzip;
2use proc_macro::TokenStream;
3use proc_macro2::Span;
4use proc_macro_crate::{crate_name, FoundCrate};
5use proc_macro_error::{abort, abort_if_dirty, emit_error, proc_macro_error};
6use quote::quote;
7use syn::punctuated::Punctuated;
8use syn::spanned::Spanned;
9use syn::{
10 parse_macro_input, Data, DeriveInput, GenericParam, Ident, LifetimeParam, Member, Token, Type,
11 TypeParam, TypeTuple,
12};
13
14#[proc_macro_error]
20#[proc_macro_attribute]
21pub fn array_as_struct(attr: TokenStream, item: TokenStream) -> TokenStream {
22 array_as_struct_helper(attr, item, false)
23}
24
25#[doc(hidden)]
26#[proc_macro_error]
27#[proc_macro_attribute]
28pub fn array_as_struct_doctest(attr: TokenStream, item: TokenStream) -> TokenStream {
29 array_as_struct_helper(attr, item, true)
30}
31
32fn array_as_struct_helper(_attr: TokenStream, item: TokenStream, doctest: bool) -> TokenStream {
33 let found_crate =
34 crate_name("array-as-struct").expect("array-as-struct is present in `Cargo.toml`");
35 let found_crate = match found_crate {
36 FoundCrate::Name(name) => Ident::new(&name, Span::call_site()),
37 FoundCrate::Itself if doctest => Ident::new("array_as_struct", Span::call_site()),
38 FoundCrate::Itself => <Token![crate]>::default().into(),
39 };
40
41 let ast = parse_macro_input!(item as DeriveInput);
42 let ast_span = ast.span();
43
44 let DeriveInput {
45 ident,
46 generics,
47 attrs,
48 vis,
49 data,
50 } = ast;
51
52 let data = match data {
53 Data::Struct(data) => data,
54 _ => abort!(ast_span, "only named-field structs are supported"),
55 };
56
57 let generic_params = generics.params;
60 let generic_params_no_attr: Punctuated<GenericParam, Token![,]> = generic_params
61 .iter()
62 .map(|gen| match gen {
63 GenericParam::Lifetime(x) => GenericParam::Lifetime(LifetimeParam {
64 attrs: vec![],
65 lifetime: x.lifetime.clone(),
66 colon_token: None,
67 bounds: Punctuated::new(),
68 }),
69 GenericParam::Type(x) => GenericParam::Type(x.clone()),
70 GenericParam::Const(x) => GenericParam::Type(TypeParam {
71 ident: x.ident.clone(),
72 attrs: vec![],
73 colon_token: None,
74 bounds: Punctuated::new(),
75 eq_token: None,
76 default: None,
77 }),
78 })
79 .collect();
80
81 let mut field_ty = None;
82 let field_info = data.fields.into_iter().map(|field| {
83 let ident = match field.ident {
84 Some(ident) => Member::Named(ident.clone()),
85 None => abort!(ast_span, "only named-field structs are supported"),
86 };
87 match field_ty.take() {
88 None => field_ty = Some(field.ty),
89 Some(field_ty) if field_ty != field.ty => {
90 emit_error!(field_ty, "type did not match future fields");
91 abort!(field.ty, "type did not match previous fields");
92 }
93 Some(x) => field_ty = Some(x),
94 }
95 (field.attrs, field.vis, ident)
96 });
97 let (attr_fields, vis_fields, ident_fields): (Vec<_>, Vec<_>, Vec<_>) = multiunzip(field_info);
98 let field_ty = field_ty.unwrap_or(Type::Tuple(TypeTuple {
99 paren_token: Default::default(),
100 elems: Punctuated::new(),
101 }));
102
103 let field_index = 0usize..;
104 let field_count = vis_fields.len();
105 let field_count_str = field_count.to_string();
106
107 abort_if_dirty();
108
109 let v = quote!(
110 #(#attrs)*
111 #[repr(transparent)]
112 #vis struct #ident<#generic_params>(
113 #[doc = #field_count_str]
115 pub [#field_ty; #field_count]
117 );
118
119 impl<#generic_params> #ident<#generic_params_no_attr> {
120 #[inline(always)]
121 #vis const fn from_val(value: <Self as #found_crate::ArrayStruct>::Value) -> Self {
123 #(#attrs)*
124 #vis struct Value<#generic_params>{#(
125 #(#attr_fields)*
126 #vis_fields #ident_fields: #field_ty
127 ),*};
128 #[allow(dead_code)]
129 #vis struct Refs<'__array_as_struct, #generic_params>{#(
130 #(#attr_fields)*
131 #vis_fields #ident_fields: &'__array_as_struct #field_ty),*
132 };
133 #[allow(dead_code)]
134 #vis struct Muts<'__array_as_struct, #generic_params>{#(
135 #(#attr_fields)*
136 #vis_fields #ident_fields: &'__array_as_struct mut #field_ty),*
137 };
138 #[allow(dead_code)]
139 #vis struct Index;
140
141 impl<#generic_params> Value<#generic_params_no_attr> {
142 #[inline(always)]
144 pub const fn to_array_struct(self) -> #ident<#generic_params_no_attr> {
145 #ident::from_val(self)
146 }
147 }
148
149 impl Index {#(
150 #[inline(always)]
151 pub const fn #ident_fields() -> usize { #field_index }
152 )*}
153
154 impl<#generic_params> #found_crate::ArrayStruct for #ident<#generic_params_no_attr> {
155 type Value = Value<#generic_params_no_attr>;
156 type Array = [#field_ty; #field_count];
157 type Refs<'__array_as_struct> = Refs<'__array_as_struct, #generic_params_no_attr>;
158 type Muts<'__array_as_struct> = Muts<'__array_as_struct, #generic_params_no_attr>;
159 type Index = Index;
160 #[inline(always)]
161 fn from_val(value: Self::Value) -> Self {
162 <#ident::<#generic_params_no_attr>>::from_val(value)
163 }
164 #[inline(always)]
165 fn val(self) -> Self::Value {
166 <#ident::<#generic_params_no_attr>>::val(self)
167 }
168 #[inline(always)]
169 fn to_array(self) -> Self::Array {
170 self.0
171 }
172 #[inline(always)]
173 fn from_array(array: Self::Array) -> Self {
174 Self(array)
175 }
176 #[inline(always)]
177 fn refs(&'_ self) -> Self::Refs<'_> {
178 <#ident::<#generic_params_no_attr>>::refs(self)
179 }
180 #[inline(always)]
181 fn muts(&'_ mut self) -> Self::Muts<'_> {
182 <#ident::<#generic_params_no_attr>>::muts(self)
183 }
184
185 }
186
187 Self([#(value.#ident_fields),*])
188 }
189
190 #[inline(always)]
191 #vis const fn val(self) -> <Self as #found_crate::ArrayStruct>::Value {
193 let Self([#(#ident_fields),*]) = self;
194 type Value = <#ident::<#generic_params_no_attr> as #found_crate::ArrayStruct>::Value;
195 Value {
196 #(#ident_fields),*
197 }
198 }
199
200 #[inline(always)]
201 #vis const fn refs(&'_ self) -> <Self as #found_crate::ArrayStruct>::Refs<'_> {
203 let Self([#(#ident_fields),*]) = self;
204 type Refs<'__array_as_struct> = <#ident::<#generic_params_no_attr> as #found_crate::ArrayStruct>::Refs<'__array_as_struct>;
205 Refs {
206 #(#ident_fields),*
207 }
208 }
209
210 #[inline(always)]
211 #vis fn muts(&'_ mut self) -> <Self as #found_crate::ArrayStruct>::Muts<'_> {
213 let Self([#(#ident_fields),*]) = self;
214 type Muts<'__array_as_struct> = <#ident::<#generic_params_no_attr> as #found_crate::ArrayStruct>::Muts<'__array_as_struct>;
215 Muts {
216 #(#ident_fields),*
217 }
218 }
219 }
220
221 impl<#generic_params> ::core::convert::From<<#ident::<#generic_params_no_attr> as #found_crate::ArrayStruct>::Value> for #ident<#generic_params_no_attr> {
222 #[inline(always)]
223 fn from(value: <#ident::<#generic_params_no_attr> as #found_crate::ArrayStruct>::Value) -> Self {
224 Self::from_val(value)
225 }
226 }
227 impl<#generic_params> ::core::convert::From<#ident<#generic_params_no_attr>> for <#ident::<#generic_params_no_attr> as #found_crate::ArrayStruct>::Value {
228 #[inline(always)]
229 fn from(strct: #ident<#generic_params_no_attr>) -> Self {
230 strct.val()
231 }
232 }
233
234 impl<#generic_params> ::core::convert::From<<#ident::<#generic_params_no_attr> as #found_crate::ArrayStruct>::Array> for #ident<#generic_params_no_attr> {
235 #[inline(always)]
236 fn from(array: <#ident::<#generic_params_no_attr> as #found_crate::ArrayStruct>::Array) -> Self {
237 Self(array)
238 }
239 }
240 impl<#generic_params> ::core::convert::From<#ident<#generic_params_no_attr>> for <#ident::<#generic_params_no_attr> as #found_crate::ArrayStruct>::Array {
241 #[inline(always)]
242 fn from(strct: #ident<#generic_params_no_attr>) -> Self {
243 strct.0
244 }
245 }
246
247 impl<#generic_params> ::core::convert::AsRef<<#ident::<#generic_params_no_attr> as #found_crate::ArrayStruct>::Array> for #ident<#generic_params_no_attr> {
248 #[inline(always)]
249 fn as_ref(&self) -> &<#ident::<#generic_params_no_attr> as #found_crate::ArrayStruct>::Array {
250 &self.0
251 }
252 }
253 impl<#generic_params> ::core::convert::AsMut<<#ident::<#generic_params_no_attr> as #found_crate::ArrayStruct>::Array> for #ident<#generic_params_no_attr> {
254 #[inline(always)]
255 fn as_mut(&mut self) -> &mut <#ident::<#generic_params_no_attr> as #found_crate::ArrayStruct>::Array {
256 &mut self.0
257 }
258 }
259
260 impl<#generic_params> ::core::borrow::Borrow<<#ident::<#generic_params_no_attr> as #found_crate::ArrayStruct>::Array> for #ident<#generic_params_no_attr> {
261 #[inline(always)]
262 fn borrow(&self) -> &<#ident::<#generic_params_no_attr> as #found_crate::ArrayStruct>::Array {
263 &self.0
264 }
265 }
266 impl<#generic_params> ::core::borrow::BorrowMut<<#ident::<#generic_params_no_attr> as #found_crate::ArrayStruct>::Array> for #ident<#generic_params_no_attr> {
267 #[inline(always)]
268 fn borrow_mut(&mut self) -> &mut <#ident::<#generic_params_no_attr> as #found_crate::ArrayStruct>::Array {
269 &mut self.0
270 }
271 }
272
273 impl<#generic_params> ::core::ops::Deref for #ident<#generic_params_no_attr> {
274 type Target = [#field_ty; #field_count];
275 #[inline(always)]
276 fn deref(&self) -> &Self::Target {
277 &self.0
278 }
279 }
280 impl<#generic_params> ::core::ops::DerefMut for #ident<#generic_params_no_attr> {
281 #[inline(always)]
282 fn deref_mut(&mut self) -> &mut Self::Target {
283 &mut self.0
284 }
285 }
286
287 impl<I> core::ops::Index<I> for #ident<#generic_params_no_attr>
288 where [#field_ty; #field_count]: core::ops::Index<I> {
289 type Output = <[#field_ty; #field_count] as core::ops::Index<I>>::Output;
290
291 #[inline(always)]
292 fn index(&self, index: I) -> &Self::Output {
293 &self.0[index]
294 }
295 }
296
297 impl<I> core::ops::IndexMut<I> for #ident<#generic_params_no_attr>
298 where [#field_ty; #field_count]: core::ops::IndexMut<I> {
299 #[inline(always)]
300 fn index_mut(&mut self, index: I) -> &mut Self::Output {
301 &mut self.0[index]
302 }
303 }
304 );
305
306 v.into()
307}