diff --git a/contracts/benchmarks/report.json b/contracts/benchmarks/report.json deleted file mode 100644 index bb94c6b60a..0000000000 --- a/contracts/benchmarks/report.json +++ /dev/null @@ -1 +0,0 @@ -[{"path":"../output/send-tx-repeat.wasm","size":901,"hasAllocator":false,"hasPanic":"none"},{"path":"../output/large-storage.wasm","size":2164,"hasAllocator":false,"hasPanic":"without message"},{"path":"../output/map-repeat.wasm","size":7234,"hasAllocator":false,"hasPanic":"without message"},{"path":"../output/queue-repeat.wasm","size":5853,"hasAllocator":false,"hasPanic":"without message"},{"path":"../output/set-repeat.wasm","size":7013,"hasAllocator":false,"hasPanic":"without message"},{"path":"../output/single-value-repeat.wasm","size":4440,"hasAllocator":false,"hasPanic":"without message"},{"path":"../output/linked-list-repeat.wasm","size":6681,"hasAllocator":true,"hasPanic":"without message"},{"path":"../output/vec-repeat.wasm","size":5231,"hasAllocator":false,"hasPanic":"without message"},{"path":"../output/str-repeat.wasm","size":2389,"hasAllocator":false,"hasPanic":"without message"},{"path":"../output/str-repeat-mb-builder-cached.wasm","size":1139,"hasAllocator":false,"hasPanic":"without message"},{"path":"../output/str-repeat-mb-builder-basic.wasm","size":759,"hasAllocator":false,"hasPanic":"none"}] \ No newline at end of file diff --git a/framework/meta/src/cli/cli_args_standalone.rs b/framework/meta/src/cli/cli_args_standalone.rs index 453b136432..9bb5aad9fc 100644 --- a/framework/meta/src/cli/cli_args_standalone.rs +++ b/framework/meta/src/cli/cli_args_standalone.rs @@ -151,15 +151,15 @@ pub struct CodeReportArgs { /// Output file path #[arg(short, long, verbatim_doc_comment)] - pub output: Option, + pub output: PathBuf, /// Output format #[arg(short, long, verbatim_doc_comment)] pub format: Option, - /// Compare + /// Compare two reports. Output available only for Markdown format #[arg(short, long, verbatim_doc_comment)] - pub compare: Option, + pub compare: Option, } #[derive(Default, Clone, PartialEq, Eq, Debug, Args)] diff --git a/framework/meta/src/cmd/code_report.rs b/framework/meta/src/cmd/code_report.rs index 26b0a15a34..f3c8ff8cbe 100644 --- a/framework/meta/src/cmd/code_report.rs +++ b/framework/meta/src/cmd/code_report.rs @@ -1,3 +1,4 @@ +pub mod compare; pub mod generate_report; pub mod render_code_report; @@ -14,8 +15,8 @@ pub fn code_report(args: &CodeReportArgs) { run_code_report( path, - args.output.as_ref().unwrap_or(&"./report.md".to_string()), + args.output.to_str().unwrap(), args.format.as_ref().unwrap_or(&OutputFormat::default()), - &args.compare.clone().unwrap_or_default(), + args.compare.clone().unwrap_or_default().to_str().unwrap(), ); } diff --git a/framework/meta/src/cmd/code_report/compare.rs b/framework/meta/src/cmd/code_report/compare.rs new file mode 100644 index 0000000000..98a72676cf --- /dev/null +++ b/framework/meta/src/cmd/code_report/compare.rs @@ -0,0 +1,87 @@ +use std::io::BufRead; + +use multiversx_sc_meta_lib::code_report_json::CodeReportJson; + +pub(crate) fn parse_into_code_report_json( + compared_file_reader: &mut dyn BufRead, +) -> Vec { + let lines = compared_file_reader.lines().skip(2); + + let mut compared_reports: Vec = Vec::new(); + + for line in lines { + match line { + Ok(l) => { + let columns: Vec = l + .split('|') + .map(|s| s.trim().to_string()) + .filter(|s| !s.is_empty()) + .collect(); + + if columns.len() == 4 { + compared_reports.push(CodeReportJson { + path: columns[0] + .split('/') + .last() + .unwrap_or_else(|| &columns[0]) + .to_owned(), + size: columns[1].parse::().unwrap(), + has_allocator: columns[2].parse::().unwrap(), + has_panic: columns[3].to_owned(), + }) + } + }, + Err(_) => return compared_reports, + } + } + + compared_reports +} + +pub(crate) fn size_status_after_comparing(size: usize, compared_size: usize) -> String { + match size.cmp(&compared_size) { + std::cmp::Ordering::Greater => { + format!("{} :arrow-right: {} :red-circle:", compared_size, size) + }, + std::cmp::Ordering::Less => { + format!("{} :arrow-right: {} :green-circle:", compared_size, size) + }, + std::cmp::Ordering::Equal => { + format!("{}", size) + }, + } +} + +pub(crate) fn allocator_status_after_comparing( + has_allocator: bool, + compared_has_allocator: bool, +) -> String { + if compared_has_allocator == has_allocator { + return format!("{}", has_allocator); + } + + let allocator_status = format!("{} :arrow-right: {}", compared_has_allocator, has_allocator); + + if !has_allocator { + format!("{allocator_status} :green-circle:") + } else { + format!("{allocator_status} :red-circle:") + } +} + +pub(crate) fn panic_status_after_comparing( + has_panic: &String, + compared_has_panic: &String, +) -> String { + if has_panic == compared_has_panic { + return has_panic.to_string(); + } + + let panic_status = format!("{} :arrow-right: {}", compared_has_panic, has_panic); + + if has_panic == "none" { + return format!("{panic_status} :green-circle:"); + } + + panic_status +} diff --git a/framework/meta/src/cmd/code_report/generate_report.rs b/framework/meta/src/cmd/code_report/generate_report.rs index 3c2fce2c6b..d921a21327 100644 --- a/framework/meta/src/cmd/code_report/generate_report.rs +++ b/framework/meta/src/cmd/code_report/generate_report.rs @@ -1,5 +1,6 @@ use std::{ - fs::{self, read_dir, File}, + fs::{read_dir, File}, + io::Write, path::PathBuf, process::Command, }; @@ -10,26 +11,25 @@ use multiversx_sc_meta_lib::{ self, code_report_json::CodeReportJson, mxsc_file_json::MxscFileJson, }; -use super::render_code_report::render_report; +use super::render_code_report::CodeReportRender; pub fn run_code_report(path: &str, output_path: &str, output_format: &OutputFormat, compare: &str) { let directors = RelevantDirectories::find_all(path, &["".to_owned()]); let reports = extract_report(directors); - let mut output = String::new(); + + let mut file = create_file(output_path); match output_format { OutputFormat::Markdown => { - render_report(&mut output, &reports, compare); + let mut render_code_report = CodeReportRender::new(&mut file, compare, &reports); + render_code_report.render_report(); }, OutputFormat::Json => { - output = serde_json::to_string(&reports).unwrap(); + let json_output = serde_json::to_string(&reports).unwrap(); + file.write_all(json_output.as_bytes()).unwrap(); }, }; - - let Ok(_) = fs::write(output_path, output) else { - return; - }; } fn extract_report(directors: RelevantDirectories) -> Vec { @@ -92,3 +92,7 @@ fn extract_reports(path: &PathBuf, reports: &mut Vec) { reports.push(data.report.code_report); } } + +fn create_file(file_path: &str) -> File { + File::create(file_path).expect("could not write report file") +} diff --git a/framework/meta/src/cmd/code_report/render_code_report.rs b/framework/meta/src/cmd/code_report/render_code_report.rs index 6d5c2763cd..a34c1be3d0 100644 --- a/framework/meta/src/cmd/code_report/render_code_report.rs +++ b/framework/meta/src/cmd/code_report/render_code_report.rs @@ -1,183 +1,132 @@ -use std::{ - fmt::Write, - fs::File, - io::{BufRead, BufReader}, -}; - -use multiversx_sc_meta_lib::code_report_json::CodeReportJson; +use core::panic; +use std::{fmt::Display, fs::File, io::BufReader}; -fn writeln_output_str>(output: &mut String, input: S) { - output.write_str(&format!("{}\n", input.as_ref())).ok(); +pub struct CodeReportRender<'a> { + pub file: Option<&'a mut dyn std::io::Write>, + pub compared_path_file: &'a str, + pub reports: &'a [CodeReportJson], } -pub fn render_report(output: &mut String, reports: &Vec, compared_path_file: &str) { - render_header(output); +use multiversx_sc_meta_lib::code_report_json::CodeReportJson; - if compared_path_file.is_empty() { - render_reports(output, reports); - } else { - render_and_compare(output, reports, compared_path_file); +use super::compare::{ + allocator_status_after_comparing, panic_status_after_comparing, parse_into_code_report_json, + size_status_after_comparing, +}; + +impl<'a> CodeReportRender<'a> { + pub fn new( + file: &'a mut dyn std::io::Write, + compared_path_file: &'a str, + reports: &'a [CodeReportJson], + ) -> Self { + Self { + file: Some(file), + compared_path_file, + reports, + } } -} -fn render_header(output: &mut String) { - writeln_output_str(output, "| Path                                                         |                                     size |                  has-allocator |                     has-format |"); - writeln_output_str(output, "| :-- | --: | --: | --: |"); -} + pub fn render_report(&mut self) { + self.render_header(); -fn render_reports(output: &mut String, reports: &Vec) { - for report in reports { - writeln_output_str( - output, - format!( - "| {} | {} | {} | {} |", - report.path.split('/').last().expect("no output path"), - report.size, - report.has_allocator, - report.has_panic - ), - ); + if self.compared_path_file.is_empty() { + self.render_reports(); + } else { + self.render_reports_and_compare(); + } } -} -fn render_and_compare(output: &mut String, reports: &[CodeReportJson], compared_path_file: &str) { - let compared_file = File::open(compared_path_file).unwrap_or_else(|_| { - panic!( - "Failed to open compared file at path: {}", - compared_path_file - ) - }); - - let mut compared_reports = Vec::new(); - if compared_path_file.ends_with("md") { - compared_reports = parse_into_code_report_json(compared_file); - } else { + fn writeln(&mut self, s: impl Display) { + let file = self.file.as_mut().unwrap(); + file.write_all(s.to_string().as_bytes()).unwrap(); + file.write_all(b"\n").unwrap(); } - for report in reports.iter() { - let path: String = report - .path - .split('/') - .last() - .expect("no output path") - .to_owned(); - if compared_reports.is_empty() { - writeln_output_str( - output, - format!( - "| {} | {} | {} | {} |", - path, report.size, report.has_allocator, report.has_panic - ), - ); - continue; - } + fn write_report_for_contract( + &mut self, + path: &String, + size: &String, + has_allocator: &String, + has_panic: &String, + ) { + self.writeln(format!( + "| {} | {} | {} | {} |", + path.split('/').last().unwrap_or_else(|| path), + size, + has_allocator, + has_panic + )); + } - if let Some(compared_report) = find_report_by_path(&compared_reports, &path) { - print_compared_output(output, report, compared_report); + fn render_header(&mut self) { + if !self.compared_path_file.is_empty() { + self.writeln(format!( + "Contract comparison with {}", + self.compared_path_file + )) } + + self.writeln("| Path                                                         |                                     size |                  has-allocator |                     has-format |"); + self.writeln("| :-- | --: | --: | --: |"); } -} -fn parse_into_code_report_json(compared_file: File) -> Vec { - let reader = BufReader::new(compared_file); - - let lines = reader.lines().skip(2); - - let mut compared_reports: Vec = Vec::new(); - - for line in lines { - match line { - Ok(l) => { - let columns: Vec = l - .split('|') - .map(|s| s.trim().to_string()) - .filter(|s| !s.is_empty()) - .collect(); - - if columns.len() == 4 { - compared_reports.push(CodeReportJson { - path: columns[0].to_owned(), - size: columns[1].parse::().unwrap(), - has_allocator: columns[2].parse::().unwrap(), - has_panic: columns[3].to_owned(), - }) - } - }, - Err(_) => return compared_reports, + fn render_reports(&mut self) { + for report in self.reports { + self.write_report_for_contract( + &report.path, + &report.size.to_string(), + &report.has_allocator.to_string(), + &report.has_panic, + ); } } - compared_reports -} - -fn find_report_by_path<'a>( - reports: &'a [CodeReportJson], - contract_path: &'a String, -) -> Option<&'a CodeReportJson> { - reports.iter().find(|&report| report.path == *contract_path) -} - -fn print_compared_output( - output: &mut String, - report: &CodeReportJson, - compared_report: &CodeReportJson, -) { - let size_report = match report.size.cmp(&compared_report.size) { - std::cmp::Ordering::Greater => { - format!( - "{} :arrow-right: {} :red-circle:", - compared_report.size, report.size - ) - }, - std::cmp::Ordering::Less => { - format!( - "{} :arrow-right: {} :green-circle:", - compared_report.size, report.size + fn render_reports_and_compare(&mut self) { + let compared_file = File::open(self.compared_path_file).unwrap_or_else(|_| { + panic!( + "Failed to open compared file at path: {}", + self.compared_path_file ) - }, - std::cmp::Ordering::Equal => { - format!("{}", report.size) - }, - }; - - let mut has_allocator_report; - if report.has_allocator == compared_report.has_allocator { - has_allocator_report = format!("{}", report.has_allocator); - } else { - has_allocator_report = format!( - "{} :arrow-right: {}", - compared_report.has_allocator, report.has_allocator - ); + }); + let mut compared_file_reader = BufReader::new(compared_file); - if !report.has_allocator { - has_allocator_report = format!("{has_allocator_report} :green-circle:"); + let compared_reports = if self.compared_path_file.ends_with("md") { + // this is only one time compare. Decide weather to exist or not + parse_into_code_report_json(&mut compared_file_reader) } else { - has_allocator_report = format!("{has_allocator_report} :red-circle:"); + serde_json::from_reader(compared_file_reader) + .unwrap_or_else(|_| panic!("Cannot deserialize into code report structure.")) + }; + + for report in self.reports.iter() { + if let Some(compared_report) = compared_reports.iter().find(|cr| { + cr.path + == report + .path + .split('/') + .last() + .unwrap_or_else(|| &report.path) + }) { + self.print_compared_output(report, compared_report); + } } } - let mut has_panic_report; - if report.has_panic == compared_report.has_panic { - has_panic_report = format!("{}", report.has_allocator); - } else { - has_panic_report = format!( - "{} :arrow-right: {}", - compared_report.has_panic, report.has_panic - ); + fn print_compared_output(&mut self, report: &CodeReportJson, compared_report: &CodeReportJson) { + let size_report = size_status_after_comparing(report.size, compared_report.size); - if report.has_panic == "none" { - has_panic_report = format!("{has_panic_report} :green-circle:"); - } - } + let has_allocator_report = + allocator_status_after_comparing(report.has_allocator, compared_report.has_allocator); - writeln_output_str( - output, - format!( - "| {} | {} | {} | {} |", - report.path.split('/').last().expect("no output path"), - size_report, - has_allocator_report, - has_panic_report - ), - ); + let has_panic_report = + panic_status_after_comparing(&report.has_panic, &compared_report.has_panic); + + self.write_report_for_contract( + &report.path, + &size_report, + &has_allocator_report, + &has_panic_report, + ); + } } diff --git a/out.md b/out.md deleted file mode 100644 index ad609f4877..0000000000 --- a/out.md +++ /dev/null @@ -1,94 +0,0 @@ -| Path                                                         |                                     size |                  has-allocator |                     has-format | -| :-- | --: | --: | --: | -| token-release.wasm | 7776 | false | without message | -| adder.wasm | 696 | false | none | -| order-book-pair.wasm | 13780 | false | without message | -| order-book-factory.wasm | 3771 | false | without message | -| nft-storage-prepay.wasm | 2295 | false | none | -| multisig.wasm | 14145 | false | without message | -| multisig-full.wasm | 15672 | false | without message | -| multisig-view.wasm | 6248 | false | without message | -| bonding-curve-contract.wasm | 14735 | false | without message | -| seed-nft-minter.wasm | 15118 | false | without message | -| rewards-distribution.wasm | 10220 | false | without message | -| crypto-bubbles.wasm | 2347 | false | none | -| kitty-auction.wasm | 10065 | false | without message | -| kitty-genetic-alg.wasm | 3147 | false | without message | -| kitty-ownership.wasm | 12731 | false | without message | -| nft-subscription.wasm | 8793 | false | without message | -| proxy-pause.wasm | 4627 | false | without message | -| lottery-esdt.wasm | 10989 | false | without message | -| esdt-transfer-with-fee.wasm | 7779 | false | without message | -| check-pause.wasm | 1228 | false | none | -| factorial.wasm | 576 | false | none | -| digital-cash.wasm | 10047 | false | without message | -| crypto-zombies.wasm | 10304 | false | without message | -| nft-minter.wasm | 10028 | false | without message | -| empty.wasm | 241 | false | none | -| fractional-nfts.wasm | 8447 | false | without message | -| crowdfunding-esdt.wasm | 3988 | false | none | -| ping-pong-egld.wasm | 6032 | false | without message | -| send-tx-repeat.wasm | 901 | false | none | -| large-storage.wasm | 2164 | false | without message | -| map-repeat.wasm | 7234 | false | without message | -| queue-repeat.wasm | 5853 | false | without message | -| set-repeat.wasm | 7013 | false | without message | -| single-value-repeat.wasm | 4440 | false | without message | -| linked-list-repeat.wasm | 6681 | true | without message | -| vec-repeat.wasm | 5231 | false | without message | -| str-repeat.wasm | 2389 | false | without message | -| str-repeat-mb-builder-cached.wasm | 1139 | false | without message | -| str-repeat-mb-builder-basic.wasm | 759 | false | none | -| big-float-features.wasm | 6468 | false | without message | -| erc20.wasm | 2133 | false | none | -| erc721.wasm | 2578 | false | none | -| lottery-erc20.wasm | 12814 | false | without message | -| erc1155-marketplace.wasm | 11172 | false | without message | -| crowdfunding-erc20.wasm | 5331 | false | without message | -| erc1155-user-mock.wasm | 1360 | false | without message | -| erc1155.wasm | 12197 | false | without message | -| scenario-tester.wasm | 1114 | false | none | -| managed-map-features.wasm | 1315 | false | none | -| use-module-view.wasm | 733 | false | none | -| use-module.wasm | 33452 | false | without message | -| rust-snippets-generator-test.wasm | 4979 | false | without message | -| basic-features.wasm | 65181 | false | without message | -| basic-features-storage-bytes.wasm | 538 | false | none | -| payable-features.wasm | 4396 | false | without message | -| promises-features.wasm | 12926 | false | without message | -| vault-promises.wasm | 8523 | false | without message | -| vault-upgrade.wasm | 700 | false | none | -| vault.wasm | 7826 | false | without message | -| first-contract.wasm | 3086 | false | without message | -| second-contract.wasm | 1355 | false | none | -| forwarder-legacy.wasm | 32330 | false | without message | -| proxy-test-second.wasm | 2105 | false | without message | -| recursive-caller.wasm | 5466 | false | without message | -| forwarder-queue-promises.wasm | 13096 | false | without message | -| forwarder-queue.wasm | 12181 | false | without message | -| forwarder.wasm | 32687 | false | without message | -| builtin-func-features.wasm | 1101 | false | none | -| local-esdt-and-nft.wasm | 12071 | false | without message | -| forwarder-raw.wasm | 15309 | false | without message | -| forwarder-raw-init-sync-call.wasm | 3332 | false | none | -| forwarder-raw-init-async-call.wasm | 3062 | false | none | -| parent.wasm | 1678 | false | none | -| child.wasm | 4057 | false | without message | -| transfer-role-features.wasm | 8775 | false | without message | -| proxy-test-first.wasm | 6031 | false | without message | -| exchange-features.wasm | 1577 | false | none | -| alloc-features.wasm | 16748 | false | without message | -| alloc-mem-leaking.wasm | 16857 | false | without message | -| alloc-mem-fail.wasm | 16682 | true | without message | -| abi-tester.wasm | 7182 | true | without message | -| abi-tester-ev.wasm | 757 | false | none | -| panic-message-features.wasm | 12875 | false | with message | -| formatted-message-features.wasm | 3470 | false | without message | -| multi-contract-features.wasm | 678 | false | none | -| multi-contract-alt-impl.wasm | 360 | false | none | -| multi-contract-example-feature.wasm | 677 | false | none | -| multi-contract-features-view.wasm | 1115 | false | none | -| rust-testing-framework-tester.wasm | 8602 | false | without message | -| esdt-system-sc-mock.wasm | 3799 | false | none | -| multiversx-wegld-swap-sc.wasm | 3557 | false | without message | -| multiversx-price-aggregator-sc.wasm | 19327 | false | without message |