1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
use crate::utils::{AttrParams, DeriveType, State};
use convert_case::{Case, Casing};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::{DeriveInput, Fields, Ident, Result};
pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result<TokenStream> {
let state = State::with_attr_params(
input,
trait_name,
quote!(),
String::from("unwrap"),
AttrParams {
enum_: vec!["ignore"],
variant: vec!["ignore"],
struct_: vec!["ignore"],
field: vec!["ignore"],
},
)?;
assert!(
state.derive_type == DeriveType::Enum,
"Unwrap can only be derived for enums"
);
let enum_name = &input.ident;
let (imp_generics, type_generics, where_clause) = input.generics.split_for_impl();
let mut funcs = vec![];
for variant_state in state.enabled_variant_data().variant_states {
let variant = variant_state.variant.unwrap();
let fn_name = Ident::new(
&format_ident!("unwrap_{}", variant.ident)
.to_string()
.to_case(Case::Snake),
variant.ident.span(),
);
let variant_ident = &variant.ident;
let (data_pattern, ret_value, ret_type) = match variant.fields {
Fields::Named(_) => panic!("cannot unwrap anonymous records"),
Fields::Unnamed(ref fields) => {
let data_pattern =
(0..fields.unnamed.len()).fold(vec![], |mut a, n| {
a.push(format_ident!("field_{}", n));
a
});
let ret_type = &fields.unnamed;
(
quote! { (#(#data_pattern),*) },
quote! { (#(#data_pattern),*) },
quote! { (#ret_type) },
)
}
Fields::Unit => (quote! {}, quote! { () }, quote! { () }),
};
let other_arms = state.variant_states.iter().map(|variant| {
variant.variant.unwrap()
}).filter(|variant| {
&variant.ident != variant_ident
}).map(|variant| {
let data_pattern = match variant.fields {
Fields::Named(_) => quote! { {..} },
Fields::Unnamed(_) => quote! { (..) },
Fields::Unit => quote! {},
};
let variant_ident = &variant.ident;
quote! { #enum_name :: #variant_ident #data_pattern =>
panic!(concat!("called `", stringify!(#enum_name), "::", stringify!(#fn_name),
"()` on a `", stringify!(#variant_ident), "` value"))
}
});
let track_caller = if cfg!(feature = "track-caller") {
quote! { #[track_caller] }
} else {
quote! {}
};
let func = quote! {
#track_caller
pub fn #fn_name(self) -> #ret_type {
match self {
#enum_name ::#variant_ident #data_pattern => #ret_value,
#(#other_arms),*
}
}
};
funcs.push(func);
}
let imp = quote! {
impl #imp_generics #enum_name #type_generics #where_clause{
#(#funcs)*
}
};
Ok(imp)
}