nmacro/
lib.rs

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}