1mod attrs;
2
3use attrs::{parse_field_attrs, parse_unit_attrs};
4use quote::quote;
5use syn::{parse_macro_input, DeriveInput};
6
7#[proc_macro_derive(Deserialize, attributes(simdnbt))]
8pub fn deserialize_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
9 let input = parse_macro_input!(input as DeriveInput);
10
11 let ident = input.ident;
12
13 let field_deserializer;
14
15 match input.data {
16 syn::Data::Struct(syn::DataStruct { fields, .. }) => match fields {
17 syn::Fields::Named(syn::FieldsNamed { named, .. }) => {
18 let mut field_deserializers = Vec::<proc_macro2::TokenStream>::new();
19 for field in named {
20 let struct_field_name = field.ident.unwrap();
21
22 let mut field_attrs = parse_field_attrs(&field.attrs);
23
24 let field_name = field_attrs
25 .rename
26 .take()
27 .unwrap_or_else(|| struct_field_name.to_string());
28
29 if field_attrs.flatten {
30 field_deserializers.push(quote! {
31 #struct_field_name: simdnbt::Deserialize::from_compound(nbt)?,
32 })
33 } else {
34 let debug_ident = format!("{ident}::{struct_field_name}");
35
36 field_deserializers.push(quote! {
37 #struct_field_name: simdnbt::FromNbtTag::from_optional_nbt_tag(
38 nbt.get(#field_name)
39 )?.ok_or(simdnbt::DeserializeError::MismatchedFieldType(#debug_ident.to_owned()))?
40 });
41 }
42 }
43 field_deserializer = quote! {
44 Self {
45 #(#field_deserializers),*
46 };
47 }
48 }
49 syn::Fields::Unnamed(unnamed) => {
50 assert!(
51 unnamed.unnamed.len() == 1,
52 "Unnamed structs must only have one field",
53 );
54
55 field_deserializer = quote! {
56 Self(simdnbt::Deserialize::from_compound(nbt)?)
57 };
58 }
59 syn::Fields::Unit => todo!(),
60 },
61 syn::Data::Enum(_) => todo!(),
62 syn::Data::Union(_) => todo!(),
63 }
64
65 let generics = input.generics;
66 let where_clause = &generics.where_clause;
67
68 let struct_attrs = attrs::parse_struct_attrs(&input.attrs);
69
70 let extra_checks = if struct_attrs.deny_unknown_fields {
71 quote! {
72 if !nbt.is_empty() {
73 return Err(simdnbt::DeserializeError::UnknownField(nbt.keys().next().unwrap().to_string()));
74 }
75 }
76 } else {
77 quote! {}
78 };
79
80 let output = quote! {
81 impl #generics simdnbt::Deserialize for #ident #generics #where_clause {
82 fn from_compound(mut nbt: simdnbt::borrow::NbtCompound) -> Result<Self, simdnbt::DeserializeError> {
83 let value = #field_deserializer;
84 #extra_checks
85 Ok(value)
86 }
87 }
88 };
89
90 output.into()
91}
92
93#[proc_macro_derive(Serialize, attributes(simdnbt))]
94pub fn serialize_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
95 let input = parse_macro_input!(input as DeriveInput);
96
97 let ident = input.ident;
98
99 let mut field_serializers = Vec::<proc_macro2::TokenStream>::new();
100
101 match input.data {
102 syn::Data::Struct(syn::DataStruct { fields, .. }) => match fields {
103 syn::Fields::Named(syn::FieldsNamed { named, .. }) => {
104 for field in named {
105 let struct_field_name = field.ident.unwrap();
106
107 let mut field_attrs = parse_field_attrs(&field.attrs);
108
109 let field_name = field_attrs
110 .rename
111 .take()
112 .unwrap_or_else(|| struct_field_name.to_string());
113
114 field_serializers.push(quote! {
115 if let Some(item) = simdnbt::ToNbtTag::to_optional_nbt_tag(self.#struct_field_name) {
116 nbt.insert(#field_name, item);
117 }
118 });
119 }
120 }
121 syn::Fields::Unnamed(_) => todo!(),
122 syn::Fields::Unit => todo!(),
123 },
124 syn::Data::Enum(_) => todo!(),
125 syn::Data::Union(_) => todo!(),
126 }
127
128 let generics = input.generics;
129 let where_clause = &generics.where_clause;
130
131 let output = quote! {
132 impl #generics simdnbt::Serialize for #ident #generics #where_clause {
133 fn to_compound(self) -> simdnbt::owned::NbtCompound {
134 let mut nbt = simdnbt::owned::NbtCompound::new();
135 #(#field_serializers)*
136 nbt
137 }
138 }
139 };
140
141 output.into()
142}
143
144#[proc_macro_derive(FromNbtTag, attributes(simdnbt))]
145pub fn from_nbt_tag_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
146 let input = parse_macro_input!(input as DeriveInput);
147
148 let ident = input.ident;
149
150 let mut matchers = Vec::<proc_macro2::TokenStream>::new();
151
152 match input.data {
153 syn::Data::Struct(_) => panic!("Use #[derive(Deserialize)] instead"),
154 syn::Data::Enum(syn::DataEnum { variants, .. }) => {
155 for variant in variants {
156 match variant.fields {
157 syn::Fields::Named(_) => todo!(),
158 syn::Fields::Unnamed(_) => todo!(),
159 syn::Fields::Unit => {
160 let enum_variant_name = variant.ident;
161
162 let mut unit_attrs = parse_unit_attrs(&variant.attrs);
163
164 let variant_name = unit_attrs
165 .rename
166 .take()
167 .unwrap_or_else(|| enum_variant_name.to_string());
168
169 matchers.push(quote! {
170 #variant_name => Some(Self::#enum_variant_name),
171 });
172 }
173 }
174 }
175 }
176 syn::Data::Union(_) => todo!(),
177 }
178
179 let generics = input.generics;
180 let where_clause = &generics.where_clause;
181
182 let output = quote! {
183 impl #generics simdnbt::FromNbtTag for #ident #generics #where_clause {
184 fn from_nbt_tag(tag: simdnbt::borrow::NbtTag) -> Option<Self> {
185 match tag.string()?.to_str().as_ref() {
186 #(#matchers)*
187 _ => None,
188 }
189 }
190 }
191 };
192
193 output.into()
194}
195
196#[proc_macro_derive(ToNbtTag, attributes(simdnbt))]
197pub fn to_nbt_tag_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
198 let input = parse_macro_input!(input as DeriveInput);
199
200 let ident = input.ident;
201
202 let mut field_matchers = Vec::<proc_macro2::TokenStream>::new();
203
204 match input.data {
205 syn::Data::Struct(_) => panic!("Use #[derive(Serialize)] instead"),
206 syn::Data::Enum(syn::DataEnum { variants, .. }) => {
207 for variant in variants {
208 match variant.fields {
209 syn::Fields::Named(_) => todo!(),
210 syn::Fields::Unnamed(_) => todo!(),
211 syn::Fields::Unit => {
212 let enum_variant_name = variant.ident;
213
214 let mut unit_attrs = parse_unit_attrs(&variant.attrs);
215
216 let variant_name = unit_attrs
217 .rename
218 .take()
219 .unwrap_or_else(|| enum_variant_name.to_string());
220
221 field_matchers.push(quote! {
222 Self::#enum_variant_name => simdnbt::owned::NbtTag::String(#variant_name.into()),
223 });
224 }
225 }
226 }
227 }
228 syn::Data::Union(_) => todo!(),
229 }
230
231 let generics = input.generics;
232 let where_clause = &generics.where_clause;
233
234 let output = quote! {
235 impl #generics simdnbt::ToNbtTag for #ident #generics #where_clause {
236 fn to_nbt_tag(self) -> simdnbt::owned::NbtTag {
237 match self {
238 #(#field_matchers)*
239 }
240 }
241 }
242 };
243
244 output.into()
245}