Coverage Report

Created: 2024-06-16 17:52

/home/runner/actions-runner/_work/fuel-vm/fuel-vm/fuel-derive/src/serialize.rs
Line
Count
Source (jump to first uncovered line)
1
use proc_macro2::TokenStream as TokenStream2;
2
use quote::quote;
3
4
use crate::attribute::{
5
    should_skip_field_binding,
6
    StructAttrs,
7
};
8
9
68
fn serialize_struct(s: &synstructure::Structure) -> TokenStream2 {
10
68
    let attrs = StructAttrs::parse(s);
11
68
    let mut s = s.clone();
12
68
13
68
    assert_eq!(s.variants().len(), 1, 
"structs must have one variant"0
);
14
15
68
    let variant: &mut synstructure::VariantInfo = &mut s.variants_mut()[0];
16
182
    variant.filter(|binding| !should_skip_field_binding(binding));
17
68
18
178
    let encode_static = variant.each(|binding| {
19
178
        quote! {
20
178
            ::fuel_types::canonical::Serialize::encode_static(#binding, buffer)?;
21
178
        }
22
178
    });
23
68
24
178
    let encode_dynamic = variant.each(|binding| {
25
178
        quote! {
26
178
            ::fuel_types::canonical::Serialize::encode_dynamic(#binding, buffer)?;
27
178
        }
28
178
    });
29
68
30
178
    let size_static_code = variant.each(|binding| {
31
178
        quote! {
32
178
            size = size.saturating_add(#binding.size_static());
33
178
        }
34
178
    }
)68
;
35
36
68
    let initial_size = if attrs.prefix.is_some() {
  Branch (36:27): [Folded - Ignored]
  Branch (36:27): [True: 12, False: 56]
37
12
        quote! { let mut size = 8usize; }
38
    } else {
39
56
        quote! { let mut size = 0usize; }
40
    };
41
68
    let size_static_code = quote! { #initial_size match self { #size_static_code}; size };
42
68
43
178
    let size_dynamic_code = variant.each(|binding| {
44
178
        quote! {
45
178
            size = size.saturating_add(#binding.size_dynamic());
46
178
        }
47
178
    });
48
68
    let size_dynamic_code =
49
68
        quote! { let mut size = 0usize; match self { #size_dynamic_code}; size };
50
51
68
    let prefix = if let Some(
prefix_type12
) = attrs.prefix.as_ref() {
  Branch (51:25): [Folded - Ignored]
  Branch (51:25): [True: 12, False: 56]
52
12
        quote! {
53
12
            <_ as ::fuel_types::canonical::Serialize>::encode(&#prefix_type, buffer)?;
54
12
        }
55
    } else {
56
56
        quote! {}
57
    };
58
59
68
    s.gen_impl(quote! {
60
68
        gen impl ::fuel_types::canonical::Serialize for @Self {
61
68
            #[inline(always)]
62
68
            fn size_static(&self) -> usize {
63
68
                #size_static_code
64
68
            }
65
68
66
68
            #[inline(always)]
67
68
            fn size_dynamic(&self) -> usize {
68
68
                #size_dynamic_code
69
68
            }
70
68
71
68
            #[inline(always)]
72
68
            fn encode_static<O: ::fuel_types::canonical::Output + ?Sized>(&self, buffer: &mut O) -> ::core::result::Result<(), ::fuel_types::canonical::Error> {
73
68
                #prefix
74
68
                match self {
75
68
                    #encode_static
76
68
                };
77
68
78
68
                ::core::result::Result::Ok(())
79
68
            }
80
68
81
68
            fn encode_dynamic<O: ::fuel_types::canonical::Output + ?Sized>(&self, buffer: &mut O) -> ::core::result::Result<(), ::fuel_types::canonical::Error> {
82
68
                match self {
83
68
                    #encode_dynamic
84
68
                };
85
68
86
68
                ::core::result::Result::Ok(())
87
68
            }
88
68
        }
89
68
    })
90
68
}
91
92
16
fn serialize_enum(s: &synstructure::Structure) -> TokenStream2 {
93
16
    assert!(!s.variants().is_empty(), 
"got invalid empty enum"0
);
94
16
    let mut s = s.clone();
95
16
    let mut next_discriminant = quote! { { 0u64 } };
96
16
97
179
    s.variants_mut().iter_mut().for_each(|v| {
98
187
        v.filter(|binding| !should_skip_field_binding(binding));
99
179
    });
100
16
101
179
    let encode_static = s.variants().iter().map(|v| {
102
179
        let pat = v.pat();
103
179
104
179
        let encode_static_iter = v.bindings().iter().map(|binding| {
105
179
            quote! {
106
179
                ::fuel_types::canonical::Serialize::encode_static(#binding, buffer)?;
107
179
            }
108
179
        });
109
179
110
179
        if v.ast().discriminant.is_some() {
  Branch (110:12): [Folded - Ignored]
  Branch (110:12): [True: 125, False: 54]
111
125
            let variant_ident = v.ast().ident;
112
125
            next_discriminant = quote! { { Self::#variant_ident as u64 } };
113
125
        }
54
114
115
179
        let encode_discriminant = quote! {
116
179
            <::core::primitive::u64 as ::fuel_types::canonical::Serialize>::encode(&#next_discriminant, buffer)?;
117
179
        };
118
179
        next_discriminant = quote! { ( (#next_discriminant) + 1u64 ) };
119
179
120
179
        quote! {
121
179
            #pat => {
122
179
                #encode_discriminant
123
179
                #(
124
179
                    { #encode_static_iter }
125
179
                )*
126
179
            }
127
179
        }
128
179
    });
129
179
    let encode_dynamic = s.variants().iter().map(|v| {
130
179
        let encode_dynamic_iter = v.each(|binding| {
131
179
            quote! {
132
179
                ::fuel_types::canonical::Serialize::encode_dynamic(#binding, buffer)?;
133
179
            }
134
179
        });
135
179
        quote! {
136
179
            #encode_dynamic_iter
137
179
        }
138
179
    });
139
16
140
16
    let match_size_static: TokenStream2 = s
141
16
        .variants()
142
16
        .iter()
143
179
        .map(|variant| {
144
179
            variant.each(|binding| {
145
179
                quote! {
146
179
                    size = size.saturating_add(#binding.size_static());
147
179
                }
148
179
            })
149
179
        })
150
16
        .collect();
151
16
    let match_size_static = quote! {{
152
16
        // `repr(128)` is unstable, so because of that we can use 8 bytes.
153
16
        let mut size = 8usize;
154
16
        match self { #match_size_static } size }
155
16
    };
156
16
157
16
    let match_size_dynamic: TokenStream2 = s
158
16
        .variants()
159
16
        .iter()
160
179
        .map(|variant| {
161
179
            variant.each(|binding| {
162
179
                quote! {
163
179
                    size = size.saturating_add(#binding.size_dynamic());
164
179
                }
165
179
            })
166
179
        })
167
16
        .collect();
168
16
    let match_size_dynamic =
169
16
        quote! {{ let mut size = 0usize; match self { #match_size_dynamic } size }};
170
171
16
    let impl_code = s.gen_impl(quote! {
172
16
        gen impl ::fuel_types::canonical::Serialize for @Self {
173
16
            #[inline(always)]
174
16
            fn size_static(&self) -> usize {
175
16
                #match_size_static
176
16
            }
177
16
178
16
            #[inline(always)]
179
16
            fn size_dynamic(&self) -> usize {
180
16
                #match_size_dynamic
181
16
            }
182
16
183
16
            #[inline(always)]
184
16
            fn encode_static<O: ::fuel_types::canonical::Output + ?Sized>(&self, buffer: &mut O) -> ::core::result::Result<(), ::fuel_types::canonical::Error> {
185
16
                match self {
186
16
                    #(
187
16
                        #encode_static
188
16
                    )*,
189
16
                    _ => return ::core::result::Result::Err(::fuel_types::canonical::Error::UnknownDiscriminant),
190
16
                };
191
16
192
16
                ::core::result::Result::Ok(())
193
16
            }
194
16
195
16
            fn encode_dynamic<O: ::fuel_types::canonical::Output + ?Sized>(&self, buffer: &mut O) -> ::core::result::Result<(), ::fuel_types::canonical::Error> {
196
16
                match self {
197
16
                    #(
198
16
                        #encode_dynamic
199
16
                    )*,
200
16
                    _ => return ::core::result::Result::Err(::fuel_types::canonical::Error::UnknownDiscriminant),
201
16
                };
202
16
203
16
                ::core::result::Result::Ok(())
204
16
            }
205
16
        }
206
16
    });
207
208
16
    quote! {
209
16
        #impl_code
210
16
    }
211
16
}
212
213
/// Derives `Serialize` trait for the given `struct` or `enum`.
214
84
pub fn serialize_derive(mut s: synstructure::Structure) -> TokenStream2 {
215
84
    s.add_bounds(synstructure::AddBounds::Fields)
216
84
        .underscore_const(true);
217
84
218
84
    match s.ast().data {
219
68
        syn::Data::Struct(_) => serialize_struct(&s),
220
16
        syn::Data::Enum(_) => serialize_enum(&s),
221
0
        _ => panic!("Can't derive `Serialize` for `union`s"),
222
    }
223
84
}