neli_proc_macros/
neli_enum.rs1use proc_macro::TokenStream;
2use proc_macro2::{Span, TokenStream as TokenStream2};
3use quote::quote;
4use syn::{
5 Arm, Attribute, Expr, Ident, ItemEnum, Lit, Meta, Path, Token, Type, Variant, parse, parse_str,
6};
7
8use crate::shared::remove_bad_attrs;
9
10fn parse_type_attr(attr: Meta) -> Type {
11 if let Meta::NameValue(nv) = attr {
12 if nv.path == parse_str::<Path>("serialized_type").unwrap() {
13 if let Expr::Lit(el) = nv.value {
14 if let Lit::Str(ls) = el.lit {
15 return parse_str::<Type>(&ls.value())
16 .unwrap_or_else(|_| panic!("Invalid type supplied: {}", ls.value()));
17 }
18 }
19 }
20 }
21
22 panic!("Attribute in the form #[neli(serialized_type = \"TYPE_LITERAL_STR\")] required")
23}
24
25fn parse_enum(enm: &mut ItemEnum, ty: &Type) -> Vec<(Vec<Attribute>, Ident, Expr)> {
26 let exprs = enm
27 .variants
28 .iter_mut()
29 .map(|var| {
30 if let Some((_, expr)) = var.discriminant.take() {
31 (var.attrs.clone(), var.ident.clone(), expr)
32 } else {
33 panic!("All variants in the provided enum require an expression assignment")
34 }
35 })
36 .collect();
37 if !enm.variants.trailing_punct() {
38 enm.variants.push_punct(Token));
39 }
40 enm.variants.push_value(
41 parse::<Variant>(TokenStream::from(quote! {
42 UnrecognizedConst(#ty)
43 }))
44 .expect("Could not parse tokens as a variant"),
45 );
46 exprs
47}
48
49fn parse_from_info(
50 enum_name: Ident,
51 var_info: Vec<(Vec<Attribute>, Ident, Expr)>,
52) -> (Vec<Arm>, Vec<Arm>) {
53 let mut from_const_info = Vec::new();
54 let mut from_type_info = Vec::new();
55 for (mut attributes, ident, expr) in var_info {
56 attributes = remove_bad_attrs(attributes);
57 let mut from_const_arm = parse::<Arm>(TokenStream::from(quote! {
58 #(
59 #attributes
60 )*
61 i if i == #expr => #enum_name::#ident,
62 }))
63 .expect("Failed to parse tokens as a match arm");
64 from_const_arm.attrs = attributes.clone();
65 from_const_info.push(from_const_arm);
66
67 let mut from_type_arm = parse::<Arm>(TokenStream::from(quote! {
68 #(
69 #attributes
70 )*
71 #enum_name::#ident => #expr,
72 }))
73 .expect("Failed to parse tokens as a match arm");
74 from_type_arm.attrs = attributes.clone();
75 from_type_info.push(from_type_arm);
76 }
77 (from_const_info, from_type_info)
78}
79
80pub fn generate_neli_enum(mut enm: ItemEnum, meta: Meta) -> TokenStream2 {
81 let enum_name = enm.ident.clone();
82 let ty = parse_type_attr(meta);
83
84 let variant_info = parse_enum(&mut enm, &ty);
85 let (from_const_info, from_type_info) = parse_from_info(enum_name.clone(), variant_info);
86
87 quote! {
88 #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
89 #[allow(missing_docs)]
90 #enm
91
92 impl #enum_name {
93 pub fn is_unrecognized(&self) -> bool {
97 match *self {
98 #enum_name::UnrecognizedConst(_) => true,
99 _ => false,
100 }
101 }
102 }
103
104 impl neli::Size for #enum_name {
105 fn unpadded_size(&self) -> usize {
106 std::mem::size_of::<#ty>()
107 }
108 }
109
110 impl neli::TypeSize for #enum_name {
111 fn type_size() -> usize {
112 std::mem::size_of::<#ty>()
113 }
114 }
115
116 impl neli::ToBytes for #enum_name {
117 fn to_bytes(&self, buffer: &mut std::io::Cursor<Vec<u8>>) -> Result<(), neli::err::SerError> {
118 let bin_rep: #ty = self.into();
119 bin_rep.to_bytes(buffer)
120 }
121 }
122
123 impl neli::FromBytes for #enum_name {
124 fn from_bytes(buffer: &mut std::io::Cursor<impl AsRef<[u8]>>) -> Result<Self, neli::err::DeError> {
125 Ok(#enum_name::from(<#ty as neli::FromBytes>::from_bytes(
126 buffer
127 )?))
128 }
129 }
130
131 impl From<#ty> for #enum_name {
132 fn from(cnst: #ty) -> Self {
133 match cnst {
134 #(
135 #from_const_info
136 )*
137 i => #enum_name::UnrecognizedConst(i),
138 }
139 }
140 }
141
142 impl From<#enum_name> for #ty {
143 fn from(enm: #enum_name) -> Self {
144 match enm {
145 #(
146 #from_type_info
147 )*
148 #enum_name::UnrecognizedConst(i) => i,
149 }
150 }
151 }
152
153 impl From<&#enum_name> for #ty {
154 fn from(enm: &#enum_name) -> Self {
155 match *enm {
156 #(
157 #from_type_info
158 )*
159 #enum_name::UnrecognizedConst(i) => i,
160 }
161 }
162 }
163 }
164}