1extern crate proc_macro2;
2extern crate quote;
3extern crate syn;
4
5mod attributes;
6mod enum_with_primitive;
7mod enums;
8mod field_attrs;
9mod shared;
10mod simple_enum;
11mod structs;
12
13use enum_with_primitive::enum_with_primitive_serializer;
14use enums::enum_serializer;
15use proc_macro2::TokenStream;
16use quote::quote;
17use simple_enum::simple_enum_serializer;
18use structs::struct_serializer;
19use syn::{Data, DeriveInput, Fields, Ident, parse_macro_input};
20
21#[proc_macro_derive(Rapira, attributes(rapira, idx, primitive))]
30pub fn serializer_trait(stream: proc_macro::TokenStream) -> proc_macro::TokenStream {
31 let ast = parse_macro_input!(stream as DeriveInput);
32 let name = &ast.ident;
33 let data = &ast.data;
34 let is_debug = attributes::debug_attr(&ast.attrs);
35
36 match data {
37 Data::Struct(data_struct) => struct_serializer(data_struct, name, ast.generics, is_debug),
38 Data::Enum(data_enum) => {
39 let is_simple_enum = data_enum.variants.iter().all(|item| item.fields.is_empty());
40 if is_simple_enum {
41 simple_enum_serializer(name)
42 } else {
43 let primitive_name = attributes::get_primitive_name(&ast.attrs);
44
45 match primitive_name {
46 Some(primitive_name) => {
47 enum_with_primitive_serializer(data_enum, name, primitive_name)
48 }
49 None => {
50 let enum_static_size = attributes::enum_static_size(&ast.attrs);
51 let min_size = attributes::min_size(&ast.attrs);
52 enum_serializer(
53 data_enum,
54 name,
55 enum_static_size,
56 min_size,
57 ast.generics,
58 is_debug,
59 )
60 }
61 }
62 }
63 }
64 Data::Union(_) => {
65 panic!(
66 "unions not supported, but Rust enums is implemented Rapira trait (use Enums instead)"
67 );
68 }
69 }
70}
71
72#[proc_macro_derive(PrimitiveFromEnum, attributes(primitive))]
74pub fn derive_primitive_from_enum(stream: proc_macro::TokenStream) -> proc_macro::TokenStream {
75 let ast = parse_macro_input!(stream as DeriveInput);
76
77 let name = &ast.ident;
78 let data = &ast.data;
79
80 match data {
81 Data::Enum(data_enum) => {
82 let is_simple_enum = data_enum.variants.iter().all(|item| item.fields.is_empty());
83
84 if is_simple_enum {
85 panic!("PrimitiveFromEnum only for non simple enum allow");
86 } else {
87 let primitive_name = ast
88 .attrs
89 .iter()
90 .find_map(|attr| {
91 if !attr.path().is_ident("primitive") {
92 return None;
93 }
94
95 let ident: Ident = attr.parse_args().unwrap();
96
97 Some(ident)
98 })
99 .expect("complex enums must include primitive type name");
100
101 let len = data_enum.variants.len();
102
103 let mut get_primitive_enum: Vec<TokenStream> = Vec::with_capacity(len);
104
105 for variant in &data_enum.variants {
106 let variant_name = &variant.ident;
107
108 match &variant.fields {
109 Fields::Unit => {
110 get_primitive_enum.push(quote! {
111 #name::#variant_name => #primitive_name::#variant_name,
112 });
113 }
114 Fields::Unnamed(fields) => {
115 let len = fields.unnamed.len();
116 if len == 1 {
117 get_primitive_enum.push(quote! {
118 #name::#variant_name(_) => #primitive_name::#variant_name,
119 });
120 } else {
121 let underscores = vec![quote! { ,_ }; len - 1];
122 get_primitive_enum.push(quote! {
123 #name::#variant_name(_ #(#underscores)*) => #primitive_name::#variant_name,
124 });
125 }
126 }
127 Fields::Named(fields) => {
128 let fields = &fields
129 .named
130 .iter()
131 .map(|f| {
132 let ident = f.ident.as_ref().unwrap();
133 quote! { #ident: _, }
134 })
135 .collect::<Vec<_>>();
136 get_primitive_enum.push(quote! {
137 #name::#variant_name{ #(#fields)* } => #primitive_name::#variant_name,
138 });
139 }
140 };
141 }
142
143 let res = quote! {
144 impl From<&#name> for #primitive_name {
145 #[inline]
146 fn from(value: &#name) -> Self {
147 match value {
148 #(#get_primitive_enum)*
149 }
150 }
151 }
152 };
153
154 proc_macro::TokenStream::from(res)
155 }
156 }
157 _ => {
158 panic!("PrimitiveFromEnum only for enum allow");
159 }
160 }
161}
162
163#[proc_macro_derive(FromU8, attributes(primitive))]
164pub fn derive_from_u8(stream: proc_macro::TokenStream) -> proc_macro::TokenStream {
165 let ast = parse_macro_input!(stream as DeriveInput);
166
167 let name = &ast.ident;
168 let data = &ast.data;
169
170 match data {
171 Data::Enum(data_enum) => {
172 let is_simple_enum = data_enum.variants.iter().all(|item| item.fields.is_empty());
173 if is_simple_enum {
174 let mut variants: Vec<TokenStream> = Vec::with_capacity(data_enum.variants.len());
175 let mut try_variants: Vec<TokenStream> =
176 Vec::with_capacity(data_enum.variants.len());
177
178 for variant in &data_enum.variants {
179 let ident = &variant.ident;
180 let var = quote! {
181 u if #name::#ident == u => #name::#ident,
182 };
183 variants.push(var);
184 try_variants.push(quote! {
185 u if #name::#ident == u => Ok(#name::#ident),
186 });
187 }
188
189 let r#gen = quote! {
190 impl PartialEq<u8> for #name {
191 fn eq(&self, other: &u8) -> bool {
192 *self as u8 == *other
193 }
194 }
195 impl From<#name> for u8 {
196 fn from(e: #name) -> u8 {
197 e as u8
198 }
199 }
200 impl rapira::FromU8 for #name {
201 #[inline]
205 fn from_u8(u: u8) -> Self {
206 match u {
207 #(#variants)*
208 _ => panic!("FromU8 undefined value: {}", u),
209 }
210 }
211 }
212 impl core::convert::TryFrom<u8> for #name {
213 type Error = rapira::EnumFromU8Error;
214 fn try_from(value: u8) -> Result<Self, Self::Error> {
215 match value {
216 #(#try_variants)*
217 _ => Err(rapira::EnumFromU8Error),
218 }
219 }
220 }
221 };
222 proc_macro::TokenStream::from(r#gen)
223 } else {
224 panic!("FromU8 only for simple enum allow (without nested data)");
225 }
226 }
227 _ => {
228 panic!("FromU8 only for enum allow");
229 }
230 }
231}