Skip to content

Commit

Permalink
Merge pull request #1714 from multiversx/report-code-cli
Browse files Browse the repository at this point in the history
Report code cli
  • Loading branch information
BiancaIalangi authored Aug 13, 2024
2 parents d1f19c0 + 5c42c62 commit 8e5231d
Show file tree
Hide file tree
Showing 10 changed files with 423 additions and 13 deletions.
6 changes: 3 additions & 3 deletions framework/meta-lib/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
pub mod abi_json;
pub mod cargo_toml_contents;
pub mod cli;
mod code_report_json;
pub mod code_report_json;
pub mod contract;
pub mod ei;
pub mod ei_check_json;
pub mod esdt_attr_file_json;
mod mxsc_file_json;
pub mod mxsc_file_json;
pub mod print_util;
mod report_info_json;
pub mod report_info_json;
pub mod tools;
pub mod version;
pub mod version_history;
Expand Down
67 changes: 65 additions & 2 deletions framework/meta/src/cli/cli_args_standalone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ pub enum StandaloneCliAction {
#[command(name = "test-coverage", about = "Run test coverage and output report")]
TestCoverage(TestCoverageArgs),

#[command(name = "report", about = "Generate code report")]
CodeReportGen(CodeReportArgs),

#[command(
about = "Generates a scenario test initialized with real data fetched from the blockchain."
)]
Expand Down Expand Up @@ -115,7 +118,7 @@ pub struct TestArgs {
}

#[derive(Default, Clone, PartialEq, Eq, Debug, ValueEnum)]
pub enum TestCoverageOutputFormat {
pub enum OutputFormat {
/// Markdown pretty-print summary
#[default]
Markdown,
Expand All @@ -132,13 +135,73 @@ pub struct TestCoverageArgs {

/// Output format
#[arg(short, long, verbatim_doc_comment)]
pub format: Option<TestCoverageOutputFormat>,
pub format: Option<OutputFormat>,

/// Ignore files by path patterns
#[arg(short = 'i', long = "ignore-filename-regex", verbatim_doc_comment)]
pub ignore_filename_regex: Vec<String>,
}

#[derive(Clone, PartialEq, Eq, Debug, Args)]
pub struct CodeReportArgs {
#[command(subcommand)]
pub command: CodeReportAction,
}

#[derive(Clone, PartialEq, Eq, Debug, Subcommand)]
pub enum CodeReportAction {
#[command(name = "compile", about = "Generates the contract report.")]
Compile(CompileArgs),

#[command(name = "compare", about = "Compare two contract reports.")]
Compare(CompareArgs),

#[command(
name = "convert",
about = "Converts a contract report to a Markdown file."
)]
Convert(ConvertArgs),
}

#[derive(Clone, PartialEq, Eq, Debug, Args)]
pub struct CompileArgs {
/// Target directory where to generate code report.
#[arg(short, long, verbatim_doc_comment)]
pub path: PathBuf,

/// Path to the Markdown or JSON file where the report results will be written.
#[arg(short, long, verbatim_doc_comment)]
pub output: PathBuf,
}

#[derive(Clone, PartialEq, Eq, Debug, Args)]
pub struct CompareArgs {
/// Path to the previous version of code report JSON file
/// that will be used for comparison.
#[arg(short, long, verbatim_doc_comment)]
pub baseline: PathBuf,

/// Path to the current version of the code report JSON file
/// that will be compared.
#[arg(short, long, verbatim_doc_comment)]
pub new: PathBuf,

/// Path to the Markdown file where the comparison results will be written.
#[arg(short, long, verbatim_doc_comment)]
pub output: PathBuf,
}

#[derive(Clone, PartialEq, Eq, Debug, Args)]
pub struct ConvertArgs {
/// Path to the JSON report file that needs to be converted to Markdown format.
#[arg(short, long, verbatim_doc_comment)]
pub input: PathBuf,

/// Path to the Markdown file where the report results will be written.
#[arg(short, long, verbatim_doc_comment)]
pub output: PathBuf,
}

#[derive(Default, Clone, PartialEq, Eq, Debug, Args)]
pub struct AllArgs {
#[command(subcommand)]
Expand Down
5 changes: 5 additions & 0 deletions framework/meta/src/cli/cli_standalone_main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ use crate::cmd::retrieve_address::retrieve_address;
use clap::Parser;

use crate::cmd::all::call_all_meta;
use crate::cmd::code_report::report;
use crate::cmd::info::call_info;
use crate::cmd::install::install;
use crate::cmd::local_deps::local_deps;
use crate::cmd::scen_test_gen::test_gen_tool;
use crate::cmd::template::{create_contract, print_template_names};
use crate::cmd::test::test;
use crate::cmd::test_coverage::test_coverage;

use crate::cmd::upgrade::upgrade_sc;

/// Entry point in the program when calling it as a standalone tool.
Expand All @@ -35,6 +37,9 @@ pub async fn cli_main_standalone() {
Some(StandaloneCliAction::TestCoverage(args)) => {
test_coverage(args);
},
Some(StandaloneCliAction::CodeReportGen(args)) => {
report(args);
},
Some(StandaloneCliAction::Account(args)) => {
retrieve_address(args).await;
},
Expand Down
1 change: 1 addition & 0 deletions framework/meta/src/cmd.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod all;
pub mod code_report;
pub mod info;
pub mod install;
pub mod local_deps;
Expand Down
15 changes: 15 additions & 0 deletions framework/meta/src/cmd/code_report.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
pub mod compare;
pub mod generate_report;
pub mod render_code_report;

use generate_report::{compare_report, convert_report, create_report};

use crate::cli::{CodeReportAction, CodeReportArgs};

pub fn report(args: &CodeReportArgs) {
match &args.command {
CodeReportAction::Compile(compile_args) => create_report(compile_args),
CodeReportAction::Compare(compare_args) => compare_report(compare_args),
CodeReportAction::Convert(convert_args) => convert_report(convert_args),
}
}
47 changes: 47 additions & 0 deletions framework/meta/src/cmd/code_report/compare.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
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
}
159 changes: 159 additions & 0 deletions framework/meta/src/cmd/code_report/generate_report.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
use std::{
fs::{read_dir, File},
io::{BufReader, Write},
path::{Path, PathBuf},
};

use crate::{
cli::{CompareArgs, CompileArgs, ConvertArgs},
folder_structure::RelevantDirectories,
};

use multiversx_sc_meta_lib::{
self, code_report_json::CodeReportJson, mxsc_file_json::MxscFileJson,
};

use super::render_code_report::CodeReportRender;

const JSON: &str = ".json";
const MD: &str = ".md";

pub fn compare_report(compare_args: &CompareArgs) {
if !is_path_ends_with(&compare_args.output, MD) {
panic!("Compare output is only available for Markdown file extension.");
}

if !is_path_ends_with(&compare_args.baseline, JSON)
&& !is_path_ends_with(&compare_args.new, JSON)
{
panic!("Compare baseline and new are only available for JSON file extension.");
}

let mut output_file = create_file(&compare_args.output);

let baseline_reports: Vec<CodeReportJson> = match File::open(&compare_args.baseline) {
Ok(_) => extract_reports_from_json(&compare_args.baseline),
Err(_) => vec![],
};

let new_reports: Vec<CodeReportJson> = extract_reports_from_json(&compare_args.new);

let mut render_code_report =
CodeReportRender::new(&mut output_file, &baseline_reports, &new_reports);
render_code_report.compare_reports();
}

pub fn convert_report(convert_args: &ConvertArgs) {
if !is_path_ends_with(&convert_args.output, MD) {
panic!("Conversion output is only available for Markdown file extension");
}

if !is_path_ends_with(&convert_args.input, JSON) {
panic!("Conversion only available from JSON file extension");
}

let mut output_file = create_file(&convert_args.output);

let reports: Vec<CodeReportJson> = extract_reports_from_json(&convert_args.input);

let mut convert_code_report = CodeReportRender::new_without_compare(&mut output_file, &reports);

convert_code_report.render_report();
}

pub fn create_report(compile_args: &CompileArgs) {
if !is_path_ends_with(&compile_args.output, JSON)
&& !is_path_ends_with(&compile_args.output, MD)
{
panic!("Create report is only available for Markdown or JSON output file.")
}

let reports = generate_new_report(&compile_args.path);

let mut file = create_file(&compile_args.output);

if is_path_ends_with(&compile_args.output, MD) {
let mut render_code_report = CodeReportRender::new_without_compare(&mut file, &reports);
render_code_report.render_report();
} else {
let json_output = serde_json::to_string(&reports).unwrap();
file.write_all(json_output.as_bytes()).unwrap();
}
}

fn generate_new_report(path: &PathBuf) -> Vec<CodeReportJson> {
let directors = RelevantDirectories::find_all(path, &["".to_owned()]);

assemble_report_vec(directors)
}

fn assemble_report_vec(directors: RelevantDirectories) -> Vec<CodeReportJson> {
let mut reports: Vec<CodeReportJson> = Vec::new();

for director in directors.iter() {
let output_path: PathBuf = director.path.join("output");

collect_reports(&output_path, &mut reports);
sanitize_output_path_from_report(&mut reports);
}

reports
}

fn find_mxsc_files(path: &PathBuf) -> Vec<PathBuf> {
if !path.is_dir() {
return vec![];
}

let mut mxsc_files = Vec::new();
for entry in read_dir(path).unwrap() {
let file_path = entry.unwrap().path();
if file_path.to_str().unwrap().ends_with(".mxsc.json") {
mxsc_files.push(file_path);
}
}

mxsc_files
}

fn collect_reports(path: &PathBuf, reports: &mut Vec<CodeReportJson>) {
for mxsc_path in find_mxsc_files(path) {
let mxsc_file = match File::open(mxsc_path) {
Ok(file) => file,
Err(_) => continue,
};
let data: MxscFileJson = serde_json::from_reader(mxsc_file).unwrap();
reports.push(data.report.code_report);
}
}

fn create_file(file_path: &PathBuf) -> File {
File::create(file_path).expect("could not write report file")
}

fn sanitize_output_path_from_report(reports: &mut [CodeReportJson]) {
reports.iter_mut().for_each(|report| {
report.path = report
.path
.split('/')
.last()
.unwrap_or(&report.path)
.to_string();
})
}

fn is_path_ends_with(path: &Path, extension: &str) -> bool {
path.to_path_buf()
.into_os_string()
.into_string()
.unwrap()
.ends_with(extension)
}

fn extract_reports_from_json(path: &PathBuf) -> Vec<CodeReportJson> {
let file =
File::open(path).unwrap_or_else(|_| panic!("file with path {} not found", path.display()));
let reader = BufReader::new(file);

serde_json::from_reader(reader).unwrap_or_else(|_| vec![])
}
Loading

0 comments on commit 8e5231d

Please sign in to comment.