azalea_buf_macros/
read.rs1use quote::{ToTokens, quote};
2use syn::{Data, Field, FieldsNamed, Ident, punctuated::Punctuated, token::Comma};
3
4pub fn create_impl_azalearead(ident: &Ident, data: &Data) -> proc_macro2::TokenStream {
5 match data {
6 syn::Data::Struct(syn::DataStruct { fields, .. }) => match fields {
7 syn::Fields::Named(FieldsNamed { named, .. }) => {
8 let (read_fields, read_field_names) = read_named_fields(named);
9
10 quote! {
11 impl azalea_buf::AzaleaRead for #ident {
12 fn azalea_read(buf: &mut std::io::Cursor<&[u8]>) -> Result<Self, azalea_buf::BufReadError> {
13 #(#read_fields)*
14 Ok(Self {
15 #(#read_field_names: #read_field_names),*
16 })
17 }
18 }
19 }
20 }
21 syn::Fields::Unit => {
22 quote! {
23 impl azalea_buf::AzaleaRead for #ident {
24 fn azalea_read(buf: &mut std::io::Cursor<&[u8]>) -> Result<Self, azalea_buf::BufReadError> {
25 Ok(Self)
26 }
27 }
28 }
29 }
30 syn::Fields::Unnamed(fields) => {
31 let read_fields = read_unnamed_fields(&fields.unnamed);
32
33 quote! {
34 impl azalea_buf::AzaleaRead for #ident {
35 fn azalea_read(buf: &mut std::io::Cursor<&[u8]>) -> Result<Self, azalea_buf::BufReadError> {
36 Ok(Self(
37 #(#read_fields),*
38 ))
39 }
40 }
41 }
42 }
43 },
44 syn::Data::Enum(syn::DataEnum { variants, .. }) => {
45 let mut match_contents = quote!();
46 let mut variant_discrim: u32 = 0;
47 let mut first = true;
48 let mut first_reader = None;
49 for variant in variants {
50 let variant_name = &variant.ident;
51 match &variant.discriminant.as_ref() {
52 Some(d) => {
53 variant_discrim = match &d.1 {
54 syn::Expr::Lit(e) => match &e.lit {
55 syn::Lit::Int(i) => i.base10_parse().unwrap(),
56 _ => panic!("Error parsing enum discriminant as int (is {e:?})"),
57 },
58 syn::Expr::Unary(_) => {
59 panic!("Negative enum discriminants are not supported")
60 }
61 _ => {
62 panic!("Error parsing enum discriminant as literal (is {:?})", d.1)
63 }
64 }
65 }
66 None => {
67 if !first {
68 variant_discrim += 1;
69 }
70 }
71 }
72 let reader = match &variant.fields {
73 syn::Fields::Named(f) => {
74 let (read_fields, read_field_names) = read_named_fields(&f.named);
75
76 quote! {
77 #(#read_fields)*
78 Ok(#ident::#variant_name {
79 #(#read_field_names: #read_field_names),*
80 })
81 }
82 }
83 syn::Fields::Unnamed(fields) => {
84 let mut reader_code = quote! {};
85 for f in &fields.unnamed {
86 let is_variable_length =
87 f.attrs.iter().any(|a| a.path().is_ident("var"));
88 let limit =
89 f.attrs
90 .iter()
91 .find(|a| a.path().is_ident("limit"))
92 .map(|a| {
93 a.parse_args::<syn::LitInt>()
94 .unwrap()
95 .base10_parse::<usize>()
96 .unwrap()
97 });
98
99 if is_variable_length && limit.is_some() {
100 panic!("Fields cannot have both var and limit attributes");
101 }
102
103 if is_variable_length {
104 reader_code.extend(quote! {
105 Self::#variant_name(azalea_buf::AzaleaReadVar::azalea_read_var(buf)?),
106 });
107 } else if let Some(limit) = limit {
108 reader_code.extend(quote! {
109 Self::#variant_name(azalea_buf::AzaleaReadLimited::azalea_read_limited(buf, #limit)?),
110 });
111 } else {
112 reader_code.extend(quote! {
113 Self::#variant_name(azalea_buf::AzaleaRead::azalea_read(buf)?),
114 });
115 }
116 }
117 quote! { Ok(#reader_code) }
118 }
119 syn::Fields::Unit => quote! {
120 Ok(Self::#variant_name)
121 },
122 };
123 if first {
124 first_reader = Some(reader.clone());
125 first = false;
126 };
127
128 match_contents.extend(quote! {
129 #variant_discrim => {
130 #reader
131 },
132 });
133 }
134
135 let first_reader = first_reader.expect("There should be at least one variant");
136
137 quote! {
138 impl azalea_buf::AzaleaRead for #ident {
139 fn azalea_read(buf: &mut std::io::Cursor<&[u8]>) -> Result<Self, azalea_buf::BufReadError> {
140 let id = azalea_buf::AzaleaReadVar::azalea_read_var(buf)?;
141 Self::azalea_read_id(buf, id)
142 }
143 }
144
145 impl #ident {
146 pub fn azalea_read_id(buf: &mut std::io::Cursor<&[u8]>, id: u32) -> Result<Self, azalea_buf::BufReadError> {
147 match id {
148 #match_contents
149 _ => {#first_reader}
151 }
152 }
153 }
154 }
155 }
156 _ => panic!("#[derive(AzBuf)] can only be used on structs"),
157 }
158}
159
160fn read_named_fields(
161 named: &Punctuated<Field, Comma>,
162) -> (Vec<proc_macro2::TokenStream>, Vec<&Option<Ident>>) {
163 let read_fields = named
164 .iter()
165 .map(|f| {
166 let field_name = &f.ident;
167
168 let reader_call = get_reader_call(f);
169 quote! { let #field_name = #reader_call; }
170 })
171 .collect::<Vec<_>>();
172 let read_field_names = named.iter().map(|f| &f.ident).collect::<Vec<_>>();
173
174 (read_fields, read_field_names)
175}
176
177fn read_unnamed_fields(unnamed: &Punctuated<Field, Comma>) -> Vec<proc_macro2::TokenStream> {
178 let read_fields = unnamed
179 .iter()
180 .map(|f| {
181 let reader_call = get_reader_call(f);
182 quote! { #reader_call }
183 })
184 .collect::<Vec<_>>();
185
186 read_fields
187}
188
189fn get_reader_call(f: &Field) -> proc_macro2::TokenStream {
190 let is_variable_length = f
191 .attrs
192 .iter()
193 .any(|a: &syn::Attribute| a.path().is_ident("var"));
194 let limit = f
195 .attrs
196 .iter()
197 .find(|a| a.path().is_ident("limit"))
198 .map(|a| {
199 a.parse_args::<syn::LitInt>()
200 .unwrap()
201 .base10_parse::<usize>()
202 .unwrap()
203 });
204
205 if is_variable_length && limit.is_some() {
206 panic!("Fields cannot have both var and limit attributes");
207 }
208
209 let field_type = &f.ty;
210
211 let reader_call = match field_type {
214 syn::Type::Path(_) | syn::Type::Array(_) => {
215 if is_variable_length {
216 quote! {
217 azalea_buf::AzaleaReadVar::azalea_read_var(buf)?
218 }
219 } else if let Some(limit) = limit {
220 quote! {
221 azalea_buf::AzaleaReadLimited::azalea_read_limited(buf, #limit)?
222 }
223 } else {
224 quote! {
225 azalea_buf::AzaleaRead::azalea_read(buf)?
226 }
227 }
228 }
229 _ => panic!(
230 "Error reading field {:?}: {}",
231 f.ident.clone(),
232 field_type.to_token_stream()
233 ),
234 };
235
236 reader_call
237}