1extern crate proc_macro;
145
146use proc_macro2::{TokenStream, Span, Ident};
147use quote::quote;
148use syn::{
149 self,
150 parse_macro_input,
151};
152
153
154fn impl_display_item(meta_list: &syn::MetaList) -> TokenStream {
155 let mut attr_list = TokenStream::new();
156
157 let fmt = match &meta_list.nested[0] {
158 syn::NestedMeta::Lit(syn::Lit::Str(v)) => v.value(),
159 _ => panic!("first attribute shoud be literal"),
160 };
161 attr_list.extend(quote! { #fmt });
162
163 for attr in meta_list.nested.iter().skip(1) {
164 let attr = match attr {
165 syn::NestedMeta::Lit(syn::Lit::Int(v)) => v.base10_parse::<u32>().unwrap(),
166 _ => panic!("attributes should be number"),
167 };
168
169 let attr_id = Ident::new(&format!("i{}", attr), Span::call_site());
170 attr_list.extend(quote! { , #attr_id });
171 }
172
173 attr_list
174}
175
176
177struct ErrorRules {
178 enum_id: Ident,
179 prefix: String,
180 from_list: TokenStream,
181 source_list: TokenStream,
182 display_list: TokenStream,
183}
184
185
186impl ErrorRules {
187 fn new(ident: &Ident) -> ErrorRules {
188 ErrorRules {
189 enum_id: ident.clone(),
190 prefix: String::default(),
191 from_list: TokenStream::default(),
192 source_list: TokenStream::default(),
193 display_list: TokenStream::default(),
194 }
195 }
196
197 fn impl_error_from_fields(&mut self,
198 item_id: &TokenStream,
199 variant: &syn::Variant)
200 {
201 let enum_id = &self.enum_id;
202
203 match &variant.fields {
204 syn::Fields::Unnamed(fields) => {
205 if fields.unnamed.len() != 1 {
206 panic!("variant should contain one field")
207 }
208 let field = &fields.unnamed[0];
209 let ty = &field.ty;
210 self.from_list.extend(quote! {
211 impl From<#ty> for #enum_id {
212 #[inline]
213 fn from(e: #ty) -> #enum_id { #item_id ( e ) }
214 }
215 });
216 self.source_list.extend(quote! {
217 #item_id (i0) => Some(i0),
218 });
219 }
220 _ => panic!("field format mismatch"),
221 };
222 }
223
224 fn impl_error_from_path(&mut self,
225 item_id: &TokenStream,
226 variant: &syn::Variant)
227 {
228 self.impl_error_from_fields(&item_id, variant);
229
230 self.display_list.extend(quote! {
231 #item_id ( i0 ) => write!(f, "{}", i0),
232 });
233 }
234
235 fn impl_error_from_list(&mut self,
236 item_id: &TokenStream,
237 variant: &syn::Variant,
238 meta_list: &syn::MetaList)
239 {
240 if meta_list.nested.is_empty() {
241 self.impl_error_from_path(item_id, variant);
242 return
243 }
244
245 self.impl_error_from_fields(item_id, variant);
246
247 let w = impl_display_item(meta_list);
248 self.display_list.extend(quote! {
249 #item_id ( i0 ) => write!(f, #w),
250 });
251 }
252
253 fn impl_error_from(&mut self,
254 item_id: &TokenStream,
255 variant: &syn::Variant,
256 meta: &syn::Meta)
257 {
258 match meta {
259 syn::Meta::Path(_) => self.impl_error_from_path(item_id, variant),
260 syn::Meta::List(v) => self.impl_error_from_list(item_id, variant, v),
261 _ => panic!("meta format mismatch"),
262 }
263 }
264
265 fn impl_error_kind_list(&mut self,
266 item_id: &TokenStream,
267 variant: &syn::Variant,
268 meta_list: &syn::MetaList)
269 {
270 if meta_list.nested.is_empty() {
271 panic!("meta format mismatch")
272 }
273
274 match &variant.fields {
275 syn::Fields::Unit => {
276 let w = impl_display_item(meta_list);
277 self.display_list.extend(quote! {
278 #item_id => write!(f, #w),
279 });
280 }
281 syn::Fields::Unnamed(fields) => {
282 let mut ident_list = TokenStream::new();
283 for i in 0 .. fields.unnamed.len() {
284 let field_id = Ident::new(&format!("i{}", i), Span::call_site());
285 ident_list.extend(quote! { #field_id, });
286 }
287
288 let w = impl_display_item(meta_list);
289 self.display_list.extend(quote! {
290 #item_id ( #ident_list ) => write!(f, #w),
291 });
292 }
293 _ => panic!("field format mismatch"),
294 };
295 }
296
297 fn impl_error_kind(&mut self,
298 item_id: &TokenStream,
299 variant: &syn::Variant,
300 meta: &syn::Meta)
301 {
302 match meta {
303 syn::Meta::List(v) => self.impl_error_kind_list(item_id, variant, v),
304 _ => panic!("meta format mismatch"),
305 }
306 }
307
308 fn impl_variant(&mut self, variant: &syn::Variant) {
309 let enum_id = &self.enum_id;
310 let item_id = &variant.ident;
311 let item_id = quote! { #enum_id::#item_id };
312
313 for attr in variant.attrs.iter().filter(|v| v.path.segments.len() == 1) {
314 match attr.path.segments[0].ident.to_string().as_str() {
315 "error_from" => {
316 let meta = attr.parse_meta().unwrap();
317 self.impl_error_from(&item_id, variant, &meta);
318 break
319 }
320 "error_kind" => {
321 let meta = attr.parse_meta().unwrap();
322 self.impl_error_kind(&item_id, variant, &meta);
323 break
324 }
325 _ => {},
326 }
327 }
328 }
329
330 fn build(&mut self, data: &syn::DataEnum) -> TokenStream {
331 for variant in &data.variants {
332 self.impl_variant(variant);
333 }
334
335 let enum_id = &self.enum_id;
336 let display_list = &self.display_list;
337 let source_list = &self.source_list;
338 let from_list = &self.from_list;
339
340 let mut display_prefix = TokenStream::new();
341 if ! self.prefix.is_empty() {
342 let prefix = &self.prefix;
343 display_prefix.extend(quote! {
344 write!(f, "{}: ", #prefix)?;
345 });
346 }
347
348 quote! {
349 impl std::fmt::Display for #enum_id {
350 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
351 #display_prefix
352 match self {
353 #display_list
354 }
355 }
356 }
357
358 impl std::error::Error for #enum_id {
359 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
360 match self {
361 #source_list
362 _ => None,
363 }
364 }
365 }
366
367 impl From<#enum_id> for std::io::Error {
368 fn from(error: #enum_id) -> Self {
369 Self::new(std::io::ErrorKind::Other, error)
370 }
371 }
372
373 #from_list
374 }
375 }
376
377 fn set_attrs(&mut self, attrs: &Vec<syn::Attribute>) {
378 for attr in attrs.iter().filter(|v| v.path.segments.len() == 1) {
379 match attr.path.segments[0].ident.to_string().as_str() {
380 "error_prefix" => {
381 if let syn::Meta::NameValue(v) = &attr.parse_meta().unwrap() {
382 if let syn::Lit::Str(v) = &v.lit {
383 self.prefix = v.value();
384 break
385 }
386 }
387 panic!("meta format mismatch")
388 }
389 _ => {},
390 }
391 }
392 }
393}
394
395
396#[proc_macro_derive(Error, attributes(error_from, error_kind, error_prefix))]
397pub fn error_rules_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
398 let input = parse_macro_input!(input as syn::DeriveInput);
399
400 if let syn::Data::Enum(ref s) = input.data {
401 let mut error_rules = ErrorRules::new(&input.ident);
402 error_rules.set_attrs(&input.attrs);
403 error_rules.build(s).into()
404 } else {
405 panic!("enum required")
406 }
407}