1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use cargo_metadata::{MetadataCommand, CargoOpt};
5
6use quote::quote;
7use syn::*;
8
9#[proc_macro_derive(NaumiConvert)]
10pub fn convert(input: TokenStream) -> TokenStream {
11 let ast = parse_macro_input!(input as DeriveInput);
12 let name = &ast.ident;
13
14 let _metadata = MetadataCommand::new()
15 .manifest_path("./Cargo.toml")
16 .features(CargoOpt::NoDefaultFeatures).exec().unwrap();
17
18 let (mut _net, mut _net_async) = (false, false);
19
20 if let Some(deps) = _metadata.root_package() {
21 for d in &deps.dependencies {
22 if d.name == "naumi" {
23 for feature in &d.features {
24 if feature == &"net".to_string() {
25 _net = true;
26 }
27 if feature == &"net_async".to_string() {
28 _net_async = true;
29 }
30 }
31 }
32 }
33 }
34 let net = if _net {
35 quote! {
36 fn send<T: std::io::Write>(&mut self, tx: &mut T) -> std::io::Result<()> {
37 naumi::types::net::send(self, tx)
38 }
39 fn receive<T: std::io::Read>(rx: &mut T) -> std::io::Result<Self> {
40 naumi::types::net::receive(rx)
41 }
42 }
43 } else {
44 quote! {}
45 };
46
47 let net_async = if _net_async {
48 quote! {
49 async fn async_send<T: tokio::io::AsyncWriteExt + core::marker::Unpin + tokio::io::AsyncWrite>(&mut self, tx: &mut T) -> std::io::Result<()> {
50 naumi::types::net::async_send(self, tx).await
51 }
52 async fn async_receive<T: tokio::io::AsyncReadExt + core::marker::Unpin + tokio::io::AsyncRead>(rx: &mut T) -> std::io::Result<Self> {
53 naumi::types::net::async_receive(rx).await
54 }
55 }
56 } else {
57 quote! {}
58 };
59
60 let expanded = match &ast.data {
61 Data::Struct(data_struct) => {
62 match &data_struct.fields {
63 Fields::Named(fields) => {
64 let field_to_bytes = fields.named.iter().rev().map(|field| {
65 let field_name = &field.ident;
66 quote! {
67 self.#field_name.to_bytes(tx);
68 }
69 });
70
71 let field_from_bytes = fields.named.iter().map(|field| {
72 let field_name = &field.ident;
73 let field_type = &field.ty;
74 quote! {
75 #field_name: <#field_type as naumi::types::Convert>::from_bytes(rx)?,
76 }
77 });
78
79 quote! {
80 impl naumi::types::Convert for #name {
81 fn to_bytes(&self, tx: &mut Vec<u8>) { #(#field_to_bytes)* }
82 fn to_bytes_return(&self) -> Vec<u8> {
83 let mut tx = vec![];
84 &self.to_bytes(&mut tx);
85 tx
86 }
87 fn from_bytes(rx: &mut Vec<u8>) -> std::io::Result<Self> {
88 Ok(Self { #(#field_from_bytes)* })
89 }
90 #net
91 #net_async
92 }
93 }
94 },
95 Fields::Unnamed(_) => {
96 let field_to_bytes = data_struct.fields.iter().enumerate().rev().map(|(index, _)| {
97 let index_lit = proc_macro2::Literal::usize_unsuffixed(index);
98 quote! { self.#index_lit.to_bytes(tx); }
99 });
100
101 let field_from_bytes = data_struct.fields.iter().enumerate().map(|(_, field)| {
102 let field_type = &field.ty;
103 quote! { <#field_type as naumi::types::Convert>::from_bytes(rx)? }
104 });
105
106 quote! {
107 impl naumi::types::Convert for #name {
108 fn to_bytes(&self, tx: &mut Vec<u8>) { #(#field_to_bytes)* }
109 fn to_bytes_return(&self) -> Vec<u8> {
110 let mut tx = vec![];
111 &self.to_bytes(&mut tx);
112 tx
113 }
114 fn from_bytes(rx: &mut Vec<u8>) -> std::io::Result<Self> {
115 Ok(Self( #(#field_from_bytes),* ))
116 }
117 #net
118 #net_async
119 }
120 }
121 },
122
123 Fields::Unit => {
124 panic!("Unit structs are not supported.");
125 },
126 }
127 },
128 Data::Enum(data_enum) => {
129 if data_enum.variants.len() > 255 {
130 panic!("Enums with more than 255 variants are not supported due to the limit of u8.");
131 }
132
133 let vars = data_enum.variants.len() as u8;
134
135 let variants = data_enum.variants.iter().enumerate().map(|(index, v)| {
136 let variant_name = &v.ident;
137 let index = index as u8;
138
139 match &v.fields {
140 Fields::Unit => quote! {
141 #name::#variant_name => tx.push(#index),
142 },
143 Fields::Unnamed(_) => {
144 quote! {
145 #name::#variant_name( field ) => {
146 field.to_bytes(tx);
147 tx.push(#index);
148 }
149 }
150 },
151 Fields::Named(_) => panic!("Named fields in enum variants are not supported."),
152 }
153 });
154
155 let from_variants = data_enum.variants.iter().enumerate().map(|(index, v)| {
156 let variant_name = &v.ident;
157 let index = index as u8;
158
159
160 if !(index == vars-1 && index != 255){
161 match &v.fields {
162 Fields::Unit => quote! {
163 #index => #name::#variant_name,
164 },
165 Fields::Unnamed(fields) => {
166 let field_type = &fields.unnamed.first().unwrap().ty;
167 quote! {
168 #index => {
169 let field = <#field_type as naumi::types::Convert>::from_bytes(rx)?;
170 #name::#variant_name(field)
171 }
172 }
173 },
174 Fields::Named(_) => panic!("Named fields in enum variants are not supported."),
175 }
176 } else {
177 match &v.fields {
178 Fields::Unit => quote! {
179 #index => #name::#variant_name,
180 _ => return Err(std::io::Error::from(std::io::ErrorKind::InvalidData)),
181 },
182 Fields::Unnamed(fields) => {
183 let field_type = &fields.unnamed.first().unwrap().ty;
184 quote! {
185 #index => {
186 let field = <#field_type as naumi::types::Convert>::from_bytes(rx)?;
187 #name::#variant_name(field)
188 }
189 _ => return Err(std::io::Error::from(std::io::ErrorKind::InvalidData)),
190 }
191 },
192 Fields::Named(_) => panic!("Named fields in enum variants are not supported."),
193 }
194 }
195 });
196 quote! {
197 impl naumi::types::Convert for #name {
198 fn to_bytes(&self, tx: &mut Vec<u8>) {
199 match self {
200 #(#variants)*
201 }
202 }
203 fn to_bytes_return(&self) -> Vec<u8> {
204 let mut tx = vec![];
205 &self.to_bytes(&mut tx);
206 tx
207 }
208 fn from_bytes(rx: &mut Vec<u8>) -> std::io::Result<Self> {
209 Ok (
210 if let Some(u) = rx.pop() {
211 match u as u8 {
212 #(#from_variants)*
213 }
214 } else {
215 return Err(std::io::Error::from(std::io::ErrorKind::InvalidData))
216 }
217 )
218 }
219 #net
220 #net_async
221 }
222 }
223 },
224 Data::Union(_) => {
225 panic!("Union type not supported")
226 },
227 };
228
229 TokenStream::from(expanded)
230}