1use darling::FromAttributes;
2use proc_macro::TokenStream;
3use quote::quote;
4use syn::{
5 parse_macro_input, punctuated::Punctuated, token::Comma, Data, DataEnum, DeriveInput, Field,
6 Fields, Ident,
7};
8
9#[derive(FromAttributes)]
11#[darling(attributes(component), forward_attrs(allow, doc, cfg))]
12struct ComponentOpts {
13 target: u16,
15}
16
17#[proc_macro_derive(PacketComponents, attributes(component))]
40pub fn derive_componets(input: TokenStream) -> TokenStream {
41 let input: DeriveInput = parse_macro_input!(input);
42 let ident: Ident = input.ident;
43
44 let data: DataEnum = match input.data {
46 Data::Enum(data) => data,
47 ty => panic!(
48 "Expects enum for components derive dont know how to handle: {:?}",
49 ty
50 ),
51 };
52
53 let length = data.variants.len();
54 let mut values = Vec::with_capacity(length);
55 let mut from_values = Vec::with_capacity(length);
56
57 for variant in data.variants {
58 let name: Ident = variant.ident;
59
60 let target: u16 = match ComponentOpts::from_attributes(&variant.attrs) {
62 Ok(value) => value.target,
63 Err(err) => panic!("Unable to parse attributes for field '{}': {:?}", name, err),
64 };
65
66 let mut fields: Punctuated<Field, Comma> = match variant.fields {
68 Fields::Unnamed(fields) => fields.unnamed,
69 _ => panic!("Field on '{}' must be unnamed and not unit type", name),
70 };
71 if fields.len() != 1 {
72 panic!("Expected only 1 field on '{}' for component value", name);
73 }
74
75 let value = fields
77 .pop()
78 .expect("Expected one component type value")
79 .into_value();
80
81 let ty = value.ty;
82
83 values.push(quote! { Self::#name(value) => (#target, value.command()), });
85 from_values
87 .push(quote! { #target => Some(Self::#name(#ty::from_value(command, notify)?)), });
88 }
89
90 quote! {
92 impl blaze_pk::packet::PacketComponents for #ident {
93
94 fn values(&self) -> (u16, u16) {
95 use blaze_pk::packet::PacketComponent;
96 match self {
97 #(#values)*
98 }
99 }
100
101 fn from_values(component: u16, command: u16, notify: bool) -> Option<Self> {
102 use blaze_pk::packet::PacketComponent;
103 match component {
104 #(#from_values)*
105 _ => None
106 }
107 }
108 }
109 }
110 .into()
111}
112
113#[derive(FromAttributes)]
115#[darling(attributes(command), forward_attrs(allow, doc, cfg))]
116struct CommandOpts {
117 target: u16,
119 #[darling(default)]
121 notify: bool,
122}
123
124#[proc_macro_derive(PacketComponent, attributes(command))]
141pub fn derive_component(input: TokenStream) -> TokenStream {
142 let input: DeriveInput = parse_macro_input!(input);
143 let ident: Ident = input.ident;
144
145 let data: DataEnum = match input.data {
146 Data::Enum(data) => data,
147 ty => panic!(
148 "Expects enum for component derive dont know how to handle: {:?}",
149 ty
150 ),
151 };
152
153 let length = data.variants.len();
154
155 let mut from_notify_value = Vec::new();
156 let mut from_normal_value = Vec::new();
157
158 let mut command = Vec::with_capacity(length);
159
160 for variant in data.variants {
161 let name: Ident = variant.ident;
162 let CommandOpts { target, notify } = match CommandOpts::from_attributes(&variant.attrs) {
163 Ok(value) => value,
164 Err(err) => panic!(
165 "Unable to parse component options for field '{}': {:?}",
166 name, err
167 ),
168 };
169
170 command.push(quote! { Self::#name => #target, });
171
172 let list = if notify {
173 &mut from_notify_value
174 } else {
175 &mut from_normal_value
176 };
177
178 list.push(quote! { #target => Some(Self::#name), })
179 }
180
181 let from_value_notify = if from_notify_value.is_empty() {
182 quote!(None)
183 } else {
184 quote! {
185 match value {
186 #(#from_notify_value)*
187 _ => None
188 }
189 }
190 };
191
192 let from_value_normal = if from_normal_value.is_empty() {
193 quote!(None)
194 } else {
195 quote! {
196 match value {
197 #(#from_normal_value)*
198 _ => None
199 }
200 }
201 };
202
203 quote! {
205 impl blaze_pk::packet::PacketComponent for #ident {
206 fn command(&self) -> u16 {
207 match self {
208 #(#command)*
209 }
210 }
211
212 fn from_value(value: u16, notify: bool) -> Option<Self> {
213 if notify {
214 #from_value_notify
215 } else {
216 #from_value_normal
217 }
218
219 }
220 }
221 }
222 .into()
223}