borsh_derive/internals/deserialize/structs/
mod.rs
1use proc_macro2::TokenStream as TokenStream2;
2use quote::quote;
3use syn::{Fields, ItemStruct, Path};
4
5use crate::internals::{attributes::item, deserialize, generics};
6
7pub fn process(input: &ItemStruct, cratename: Path) -> syn::Result<TokenStream2> {
8 let name = &input.ident;
9 let generics = generics::without_defaults(&input.generics);
10 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
11 let mut where_clause = generics::default_where(where_clause);
12 let mut body = TokenStream2::new();
13 let mut generics_output = deserialize::GenericsOutput::new(&generics);
14
15 let return_value = match &input.fields {
16 Fields::Named(fields) => {
17 for field in &fields.named {
18 deserialize::process_field(field, &cratename, &mut body, &mut generics_output)?;
19 }
20 quote! {
21 Self { #body }
22 }
23 }
24 Fields::Unnamed(fields) => {
25 for field in fields.unnamed.iter() {
26 deserialize::process_field(field, &cratename, &mut body, &mut generics_output)?;
27 }
28 quote! {
29 Self( #body )
30 }
31 }
32 Fields::Unit => {
33 quote! {
34 Self {}
35 }
36 }
37 };
38 generics_output.extend(&mut where_clause, &cratename);
39
40 if let Some(method_ident) = item::contains_initialize_with(&input.attrs)? {
41 Ok(quote! {
42 impl #impl_generics #cratename::de::BorshDeserialize for #name #ty_generics #where_clause {
43 fn deserialize_reader<__R: #cratename::io::Read>(reader: &mut __R) -> ::core::result::Result<Self, #cratename::io::Error> {
44 let mut return_value = #return_value;
45 return_value.#method_ident();
46 Ok(return_value)
47 }
48 }
49 })
50 } else {
51 Ok(quote! {
52 impl #impl_generics #cratename::de::BorshDeserialize for #name #ty_generics #where_clause {
53 fn deserialize_reader<__R: #cratename::io::Read>(reader: &mut __R) -> ::core::result::Result<Self, #cratename::io::Error> {
54 Ok(#return_value)
55 }
56 }
57 })
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use crate::internals::test_helpers::{
64 default_cratename, local_insta_assert_snapshot, pretty_print_syn_str,
65 };
66
67 use super::*;
68
69 #[test]
70 fn simple_struct() {
71 let item_struct: ItemStruct = syn::parse2(quote! {
72 struct A {
73 x: u64,
74 y: String,
75 }
76 })
77 .unwrap();
78
79 let actual = process(&item_struct, default_cratename()).unwrap();
80
81 local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap());
82 }
83
84 #[test]
85 fn simple_struct_with_custom_crate() {
86 let item_struct: ItemStruct = syn::parse2(quote! {
87 struct A {
88 x: u64,
89 y: String,
90 }
91 })
92 .unwrap();
93
94 let crate_: Path = syn::parse2(quote! { reexporter::borsh }).unwrap();
95 let actual = process(&item_struct, crate_).unwrap();
96
97 local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap());
98 }
99
100 #[test]
101 fn simple_generics() {
102 let item_struct: ItemStruct = syn::parse2(quote! {
103 struct A<K, V> {
104 x: HashMap<K, V>,
105 y: String,
106 }
107 })
108 .unwrap();
109
110 let actual = process(&item_struct, default_cratename()).unwrap();
111 local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap());
112 }
113
114 #[test]
115 fn simple_generic_tuple_struct() {
116 let item_struct: ItemStruct = syn::parse2(quote! {
117 struct TupleA<T>(T, u32);
118 })
119 .unwrap();
120
121 let actual = process(&item_struct, default_cratename()).unwrap();
122 local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap());
123 }
124
125 #[test]
126 fn bound_generics() {
127 let item_struct: ItemStruct = syn::parse2(quote! {
128 struct A<K: Key, V> where V: Value {
129 x: HashMap<K, V>,
130 y: String,
131 }
132 })
133 .unwrap();
134
135 let actual = process(&item_struct, default_cratename()).unwrap();
136 local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap());
137 }
138
139 #[test]
140 fn recursive_struct() {
141 let item_struct: ItemStruct = syn::parse2(quote! {
142 struct CRecC {
143 a: String,
144 b: HashMap<String, CRecC>,
145 }
146 })
147 .unwrap();
148
149 let actual = process(&item_struct, default_cratename()).unwrap();
150
151 local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap());
152 }
153
154 #[test]
155 fn generic_tuple_struct_borsh_skip1() {
156 let item_struct: ItemStruct = syn::parse2(quote! {
157 struct G<K, V, U> (
158 #[borsh(skip)]
159 HashMap<K, V>,
160 U,
161 );
162 })
163 .unwrap();
164
165 let actual = process(&item_struct, default_cratename()).unwrap();
166
167 local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap());
168 }
169
170 #[test]
171 fn generic_tuple_struct_borsh_skip2() {
172 let item_struct: ItemStruct = syn::parse2(quote! {
173 struct G<K, V, U> (
174 HashMap<K, V>,
175 #[borsh(skip)]
176 U,
177 );
178 })
179 .unwrap();
180
181 let actual = process(&item_struct, default_cratename()).unwrap();
182
183 local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap());
184 }
185
186 #[test]
187 fn generic_named_fields_struct_borsh_skip() {
188 let item_struct: ItemStruct = syn::parse2(quote! {
189 struct G<K, V, U> {
190 #[borsh(skip)]
191 x: HashMap<K, V>,
192 y: U,
193 }
194 })
195 .unwrap();
196
197 let actual = process(&item_struct, default_cratename()).unwrap();
198
199 local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap());
200 }
201
202 #[test]
203 fn generic_deserialize_bound() {
204 let item_struct: ItemStruct = syn::parse2(quote! {
205 struct C<T: Debug, U> {
206 a: String,
207 #[borsh(bound(deserialize =
208 "T: PartialOrd + Hash + Eq + borsh::de::BorshDeserialize,
209 U: borsh::de::BorshDeserialize"
210 ))]
211 b: HashMap<T, U>,
212 }
213 })
214 .unwrap();
215
216 let actual = process(&item_struct, default_cratename()).unwrap();
217
218 local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap());
219 }
220
221 #[test]
222 fn test_override_automatically_added_default_trait() {
223 let item_struct: ItemStruct = syn::parse2(quote! {
224 struct G1<K, V, U>(
225 #[borsh(skip,bound(deserialize = ""))]
226 HashMap<K, V>,
227 U
228 );
229 })
230 .unwrap();
231
232 let actual = process(&item_struct, default_cratename()).unwrap();
233
234 local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap());
235 }
236
237 #[test]
238 fn check_deserialize_with_attr() {
239 let item_struct: ItemStruct = syn::parse2(quote! {
240 struct A<K: Ord, V> {
241 #[borsh(deserialize_with = "third_party_impl::deserialize_third_party")]
242 x: ThirdParty<K, V>,
243 y: u64,
244 }
245 })
246 .unwrap();
247
248 let actual = process(&item_struct, default_cratename()).unwrap();
249
250 local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap());
251 }
252 #[test]
253 fn borsh_init_func() {
254 let item_enum: ItemStruct = syn::parse2(quote! {
255 #[borsh(init=initialization_method)]
256 struct A {
257 x: u64,
258 y: String,
259 }
260 })
261 .unwrap();
262 let actual = process(&item_enum, default_cratename()).unwrap();
263 local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap());
264 }
265}