@@ -9,6 +9,7 @@ use comfy_table::{presets::ASCII_MARKDOWN, *};
9
9
use foundry_common:: { calc, TestFunctionExt } ;
10
10
use foundry_evm:: traces:: CallKind ;
11
11
use serde:: { Deserialize , Serialize } ;
12
+ use serde_json:: json;
12
13
use std:: { collections:: BTreeMap , fmt:: Display } ;
13
14
use yansi:: Paint ;
14
15
@@ -156,59 +157,136 @@ impl GasReport {
156
157
157
158
impl Display for GasReport {
158
159
fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> Result < ( ) , std:: fmt:: Error > {
159
- for ( name, contract) in & self . contracts {
160
- if contract. functions . is_empty ( ) {
161
- trace ! ( name, "gas report contract without functions" ) ;
162
- continue ;
163
- }
160
+ match self . report_type {
161
+ GasReportKind :: Markdown => {
162
+ for ( name, contract) in & self . contracts {
163
+ if contract. functions . is_empty ( ) {
164
+ trace ! ( name, "gas report contract without functions" ) ;
165
+ continue ;
166
+ }
164
167
165
- if self . report_type == GasReportKind :: JSON {
166
- writeln ! ( f, "{}" , serde_json:: to_string( & contract) . unwrap( ) ) ?;
167
- continue ;
168
+ let table = self . format_table_output ( contract, name) ;
169
+ writeln ! ( f, "{table}" ) ?;
170
+ writeln ! ( f, "\n " ) ?;
171
+ }
172
+ }
173
+ GasReportKind :: JSON => {
174
+ writeln ! ( f, "{}" , & self . format_json_output( ) ) ?;
168
175
}
169
-
170
- let mut table = Table :: new ( ) ;
171
- table. load_preset ( ASCII_MARKDOWN ) ;
172
- table. set_header ( [ Cell :: new ( format ! ( "{name} contract" ) )
173
- . add_attribute ( Attribute :: Bold )
174
- . fg ( Color :: Green ) ] ) ;
175
- table. add_row ( [
176
- Cell :: new ( "Deployment Cost" ) . add_attribute ( Attribute :: Bold ) . fg ( Color :: Cyan ) ,
177
- Cell :: new ( "Deployment Size" ) . add_attribute ( Attribute :: Bold ) . fg ( Color :: Cyan ) ,
178
- ] ) ;
179
- table. add_row ( [ contract. gas . to_string ( ) , contract. size . to_string ( ) ] ) ;
180
-
181
- table. add_row ( [
182
- Cell :: new ( "Function Name" ) . add_attribute ( Attribute :: Bold ) . fg ( Color :: Magenta ) ,
183
- Cell :: new ( "min" ) . add_attribute ( Attribute :: Bold ) . fg ( Color :: Green ) ,
184
- Cell :: new ( "avg" ) . add_attribute ( Attribute :: Bold ) . fg ( Color :: Yellow ) ,
185
- Cell :: new ( "median" ) . add_attribute ( Attribute :: Bold ) . fg ( Color :: Yellow ) ,
186
- Cell :: new ( "max" ) . add_attribute ( Attribute :: Bold ) . fg ( Color :: Red ) ,
187
- Cell :: new ( "# calls" ) . add_attribute ( Attribute :: Bold ) ,
188
- ] ) ;
189
- contract. functions . iter ( ) . for_each ( |( fname, sigs) | {
190
- sigs. iter ( ) . for_each ( |( sig, gas_info) | {
191
- // show function signature if overloaded else name
192
- let fn_display =
193
- if sigs. len ( ) == 1 { fname. clone ( ) } else { sig. replace ( ':' , "" ) } ;
194
-
195
- table. add_row ( [
196
- Cell :: new ( fn_display) . add_attribute ( Attribute :: Bold ) ,
197
- Cell :: new ( gas_info. min . to_string ( ) ) . fg ( Color :: Green ) ,
198
- Cell :: new ( gas_info. mean . to_string ( ) ) . fg ( Color :: Yellow ) ,
199
- Cell :: new ( gas_info. median . to_string ( ) ) . fg ( Color :: Yellow ) ,
200
- Cell :: new ( gas_info. max . to_string ( ) ) . fg ( Color :: Red ) ,
201
- Cell :: new ( gas_info. calls . to_string ( ) ) ,
202
- ] ) ;
203
- } )
204
- } ) ;
205
- writeln ! ( f, "{table}" ) ?;
206
- writeln ! ( f, "\n " ) ?;
207
176
}
177
+
208
178
Ok ( ( ) )
209
179
}
210
180
}
211
181
182
+ impl GasReport {
183
+ fn format_json_output ( & self ) -> String {
184
+ #[ inline]
185
+ fn format_gas_info ( gas_info : & GasInfo ) -> serde_json:: Value {
186
+ json ! ( {
187
+ "calls" : gas_info. calls,
188
+ "min" : gas_info. min,
189
+ "mean" : gas_info. mean,
190
+ "median" : gas_info. median,
191
+ "max" : gas_info. max,
192
+ } )
193
+ }
194
+
195
+ serde_json:: to_string (
196
+ & self
197
+ . contracts
198
+ . iter ( )
199
+ . filter_map ( |( name, contract) | {
200
+ if contract. functions . is_empty ( ) {
201
+ trace ! ( name, "gas report contract without functions" ) ;
202
+ return None ;
203
+ }
204
+
205
+ let functions = contract
206
+ . functions
207
+ . iter ( )
208
+ . map ( |( fname, sigs) | {
209
+ // If there is only one signature, display the gas info directly.
210
+ let function_value = if sigs. len ( ) == 1 {
211
+ format_gas_info ( sigs. values ( ) . next ( ) . unwrap ( ) )
212
+ } else {
213
+ // If there are multiple signatures, e.g. overloads like:
214
+ // - `foo(uint256)`
215
+ // - `foo(int256)`
216
+ // display the gas info as a map with the signature as the key.
217
+ let signatures = sigs
218
+ . iter ( )
219
+ . map ( |( sig, gas_info) | {
220
+ let display_name = sig. replace ( ':' , "" ) ;
221
+ ( display_name, format_gas_info ( gas_info) )
222
+ } )
223
+ . collect :: < BTreeMap < _ , _ > > ( ) ;
224
+
225
+ json ! ( signatures)
226
+ } ;
227
+
228
+ ( fname. to_string ( ) , function_value)
229
+ } )
230
+ . collect :: < BTreeMap < _ , _ > > ( ) ;
231
+
232
+ Some ( json ! ( {
233
+ "contract" : name,
234
+ "deployment" : {
235
+ "gas" : contract. gas,
236
+ "size" : contract. size,
237
+ } ,
238
+ "functions" : functions,
239
+ } ) )
240
+ } )
241
+ . collect :: < Vec < _ > > ( ) ,
242
+ )
243
+ . unwrap ( )
244
+ }
245
+
246
+ // Helper function to format the table output
247
+ fn format_table_output ( & self , contract : & ContractInfo , name : & str ) -> Table {
248
+ let mut table = Table :: new ( ) ;
249
+ table. load_preset ( ASCII_MARKDOWN ) ;
250
+ table. set_header ( [ Cell :: new ( format ! ( "{name} contract" ) )
251
+ . add_attribute ( Attribute :: Bold )
252
+ . fg ( Color :: Green ) ] ) ;
253
+
254
+ table. add_row ( [
255
+ Cell :: new ( "Deployment Cost" ) . add_attribute ( Attribute :: Bold ) . fg ( Color :: Cyan ) ,
256
+ Cell :: new ( "Deployment Size" ) . add_attribute ( Attribute :: Bold ) . fg ( Color :: Cyan ) ,
257
+ ] ) ;
258
+ table. add_row ( [ contract. gas . to_string ( ) , contract. size . to_string ( ) ] ) ;
259
+
260
+ table. add_row ( [
261
+ Cell :: new ( "Function Name" ) . add_attribute ( Attribute :: Bold ) . fg ( Color :: Magenta ) ,
262
+ Cell :: new ( "min" ) . add_attribute ( Attribute :: Bold ) . fg ( Color :: Green ) ,
263
+ Cell :: new ( "avg" ) . add_attribute ( Attribute :: Bold ) . fg ( Color :: Yellow ) ,
264
+ Cell :: new ( "median" ) . add_attribute ( Attribute :: Bold ) . fg ( Color :: Yellow ) ,
265
+ Cell :: new ( "max" ) . add_attribute ( Attribute :: Bold ) . fg ( Color :: Red ) ,
266
+ Cell :: new ( "# calls" ) . add_attribute ( Attribute :: Bold ) ,
267
+ ] ) ;
268
+
269
+ contract. functions . iter ( ) . for_each ( |( fname, sigs) | {
270
+ sigs. iter ( ) . for_each ( |( sig, gas_info) | {
271
+ // Show function signature if overloaded else display function name.
272
+ let display_name =
273
+ if sigs. len ( ) == 1 { fname. to_string ( ) } else { sig. replace ( ':' , "" ) } ;
274
+
275
+ table. add_row ( [
276
+ Cell :: new ( display_name) . add_attribute ( Attribute :: Bold ) ,
277
+ Cell :: new ( gas_info. min . to_string ( ) ) . fg ( Color :: Green ) ,
278
+ Cell :: new ( gas_info. mean . to_string ( ) ) . fg ( Color :: Yellow ) ,
279
+ Cell :: new ( gas_info. median . to_string ( ) ) . fg ( Color :: Yellow ) ,
280
+ Cell :: new ( gas_info. max . to_string ( ) ) . fg ( Color :: Red ) ,
281
+ Cell :: new ( gas_info. calls . to_string ( ) ) ,
282
+ ] ) ;
283
+ } )
284
+ } ) ;
285
+
286
+ table
287
+ }
288
+ }
289
+
212
290
#[ derive( Clone , Debug , Default , Serialize , Deserialize ) ]
213
291
pub struct ContractInfo {
214
292
pub gas : u64 ,
0 commit comments