1use quote::quote;
53
54#[proc_macro_derive(named_array)]
56pub fn named_array(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
57 let source = syn::parse_macro_input!(input as syn::DeriveInput);
58
59 let (name, fields) = if let syn::Data::Struct(data) = source.data {
60 (source.ident, data.fields)
61 } else {
62 panic!("Only structs are supported");
63 };
64
65 match fields {
66 syn::Fields::Named(fields) => make_named(name, fields),
67 syn::Fields::Unnamed(fields) => make_unnamed(name, fields),
68 _ => panic!("unit structs are not supported"),
69 }
70}
71
72fn make_named(name: syn::Ident, fields: syn::FieldsNamed) -> proc_macro::TokenStream {
73 let mut fields = fields.named.iter();
74
75 let mut errs = Vec::new();
76 let mut names = Vec::new();
77 let ty = fields
78 .next()
79 .map(|f| {
80 names.push(f.ident.as_ref().unwrap());
81 &f.ty
82 })
83 .expect("Expected at least one field");
84 for f in fields {
85 if f.ty != *ty {
86 errs.push(syn::Error::new_spanned(
87 &f.ty,
88 "All fields must have the same type",
89 ));
90 }
91 names.push(f.ident.as_ref().unwrap());
92 }
93
94 if !errs.is_empty() {
95 let errs = errs.into_iter().map(|e| e.to_compile_error());
96
97 return quote! {
100 #(#errs)*
101
102 impl ::core::ops::Index<usize> for #name {
103 type Output = #ty;
104 fn index(&self, _: usize) -> &Self::Output {
105 unimplemented!("Unable to generate code due to previous errors");
106 }
107 }
108
109 impl ::core::ops::IndexMut<usize> for #name {
110 fn index_mut(&mut self, _: usize) -> &mut Self::Output {
111 unimplemented!("Unable to generate code due to previous errors");
112 }
113 }
114 }
115 .into();
116 }
117
118 let len = names.len();
119 let panic_msg = format!("index out of bounds: the len is {len} but the index is {{}}");
120 let range1 = 0usize..;
121 let range2 = 0usize..;
122
123 quote! {
124 impl ::core::ops::Index<usize> for #name {
125 type Output = #ty;
126 fn index(&self, index: usize) -> &Self::Output {
127 match index {
128 #(
129 #range1 => &self.#names,
130 )*
131 i => panic!(#panic_msg, i),
132 }
133 }
134 }
135
136 impl ::core::ops::IndexMut<usize> for #name {
137 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
138 match index {
139 #(
140 #range2 => &mut self.#names,
141 )*
142 i => panic!(#panic_msg, i),
143 }
144 }
145 }
146 }
147 .into()
148}
149
150fn make_unnamed(name: syn::Ident, fields: syn::FieldsUnnamed) -> proc_macro::TokenStream {
151 let mut fields = fields.unnamed.iter();
152
153 let len = fields.len();
154 let mut errs = Vec::new();
155 let ty = fields
156 .next()
157 .map(|f| &f.ty)
158 .expect("Expected at least one field");
159 for f in fields {
160 if f.ty != *ty {
161 errs.push(syn::Error::new_spanned(
162 &f.ty,
163 "All fields must have the same type",
164 ));
165 }
166 }
167
168 if !errs.is_empty() {
169 let errs = errs.into_iter().map(|e| e.to_compile_error());
170 return quote! {
173 #(#errs)*
174 impl ::core::ops::Index<usize> for #name {
175 type Output = #ty;
176 fn index(&self, _: usize) -> &Self::Output {
177 unimplemented!("Unable to generate code due to previous errors");
178 }
179 }
180 impl ::core::ops::IndexMut<usize> for #name {
181 fn index_mut(&mut self, _: usize) -> &mut Self::Output {
182 unimplemented!("Unable to generate code due to previous errors");
183 }
184 }
185 }
186 .into();
187 }
188
189 let panic_msg = format!("index out of bounds: the len is {len} but the index is {{}}");
190 let range1 = 0usize..len;
191 let range2 = 0usize..len;
192 let index1 = (0usize..len).map(syn::Index::from);
193 let index2 = (0usize..len).map(syn::Index::from);
194
195 quote! {
196 impl ::core::ops::Index<usize> for #name {
197 type Output = #ty;
198 fn index(&self, index: usize) -> &Self::Output {
199 match index {
200 #( #range1 => &self.#index1, )*
201 i => panic!(#panic_msg, i),
202 }
203 }
204 }
205 impl ::core::ops::IndexMut<usize> for #name {
206 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
207 match index {
208 #( #range2 => &mut self.#index2, )*
209 i => panic!(#panic_msg, i),
210 }
211 }
212 }
213 }
214 .into()
215}