diff --git a/crates/cairo-coverage-args/src/lib.rs b/crates/cairo-coverage-args/src/lib.rs index 115c61d..286ea75 100644 --- a/crates/cairo-coverage-args/src/lib.rs +++ b/crates/cairo-coverage-args/src/lib.rs @@ -1,54 +1,23 @@ -use anyhow::ensure; -use camino::Utf8PathBuf; -use clap::{Parser, ValueEnum}; +use crate::run::RunArgs; +use clap::{Parser, Subcommand}; + +pub mod run; #[derive(Parser, Debug)] #[command(version, args_conflicts_with_subcommands = true)] pub struct CairoCoverageArgs { - /// Paths to the .json files with trace data. - #[arg(value_parser = parse_trace_file, num_args = 1.., required = true)] - pub trace_files: Vec, - - /// Path to the output file. - #[arg(short, long, default_value = "coverage.lcov")] - pub output_path: Utf8PathBuf, - - /// Include additional components in the coverage report. - #[arg(long, short, num_args = 1..)] - pub include: Vec, - - /// Path to the project directory. If not provided, the project directory is inferred from the trace. - #[arg(value_parser = parse_project_path, long)] - pub project_path: Option, -} - -/// Additional components that can be included in the coverage report. -#[derive(ValueEnum, Debug, Clone, Eq, PartialEq)] -pub enum IncludedComponent { - /// Run coverage on functions marked with `#[test]` attribute - TestFunctions, - /// Run coverage on macros and generated code by them. This includes inline macros, attribute macros, and derive macros. - Macros, + /// Arguments for the `run` subcommand so user can use + /// `cairo-coverage` without specifying the subcommand. + #[clap(flatten)] + pub run_args: RunArgs, + /// Subcommand and its arguments. + #[command(subcommand)] + pub command: Option, } -fn parse_trace_file(path: &str) -> anyhow::Result { - let trace_file = Utf8PathBuf::from(path); - - ensure!(trace_file.exists(), "Trace file does not exist"); - ensure!(trace_file.is_file(), "Trace file is not a file"); - ensure!( - matches!(trace_file.extension(), Some("json")), - "Trace file must have a JSON extension" - ); - - Ok(trace_file) -} - -fn parse_project_path(path: &str) -> anyhow::Result { - let project_path = Utf8PathBuf::from(path); - - ensure!(project_path.exists(), "Project path does not exist"); - ensure!(project_path.is_dir(), "Project path is not a directory"); - - Ok(project_path) +/// Subcommand and its arguments. +#[derive(Subcommand, Debug)] +pub enum Command { + /// Run `cairo-coverage` tool. + Run(RunArgs), } diff --git a/crates/cairo-coverage-args/src/run.rs b/crates/cairo-coverage-args/src/run.rs new file mode 100644 index 0000000..c982b62 --- /dev/null +++ b/crates/cairo-coverage-args/src/run.rs @@ -0,0 +1,54 @@ +use anyhow::{ensure, Result}; +use camino::Utf8PathBuf; +use clap::{Parser, ValueEnum}; + +/// Arguments accepted by the `run` subcommand. +#[derive(Parser, Debug)] +pub struct RunArgs { + /// Paths to the .json files with trace data. + #[arg(value_parser = parse_trace_file, num_args = 1.., required = true)] + pub trace_files: Vec, + + /// Path to the output file. + #[arg(short, long, default_value = "coverage.lcov")] + pub output_path: Utf8PathBuf, + + /// Include additional components in the coverage report. + #[arg(long, short, num_args = 1..)] + pub include: Vec, + + /// Path to the project directory. If not provided, the project directory is inferred from the trace. + #[arg(value_parser = parse_project_path, long)] + pub project_path: Option, +} + +/// Additional components that can be included in the coverage report. +#[derive(ValueEnum, Debug, Clone, Eq, PartialEq)] +pub enum IncludedComponent { + /// Run coverage on functions marked with `#[test]` attribute + TestFunctions, + /// Run coverage on macros and generated code by them. This includes inline macros, attribute macros, and derive macros. + Macros, +} + +fn parse_trace_file(path: &str) -> Result { + let trace_file = Utf8PathBuf::from(path); + + ensure!(trace_file.exists(), "Trace file does not exist"); + ensure!(trace_file.is_file(), "Trace file is not a file"); + ensure!( + matches!(trace_file.extension(), Some("json")), + "Trace file must have a JSON extension" + ); + + Ok(trace_file) +} + +fn parse_project_path(path: &str) -> Result { + let project_path = Utf8PathBuf::from(path); + + ensure!(project_path.exists(), "Project path does not exist"); + ensure!(project_path.is_dir(), "Project path is not a directory"); + + Ok(project_path) +} diff --git a/crates/cairo-coverage-core/src/input/filter/statement_category_filter.rs b/crates/cairo-coverage-core/src/input/filter/statement_category_filter.rs index 4749cdd..89c5db3 100644 --- a/crates/cairo-coverage-core/src/input/filter/statement_category_filter.rs +++ b/crates/cairo-coverage-core/src/input/filter/statement_category_filter.rs @@ -3,7 +3,7 @@ use crate::input::filter::ignore::CairoCoverageIgnoreMatcher; use crate::input::sierra_to_cairo_map::{SimpleLibfuncName, StatementOrigin}; use cairo_annotations::annotations::coverage::SourceFileFullPath; use cairo_annotations::annotations::profiler::FunctionName; -use cairo_coverage_args::IncludedComponent; +use cairo_coverage_args::run::IncludedComponent; use camino::Utf8PathBuf; use regex::Regex; use std::collections::HashSet; diff --git a/crates/cairo-coverage-core/src/lib.rs b/crates/cairo-coverage-core/src/lib.rs index 44184b3..4231596 100644 --- a/crates/cairo-coverage-core/src/lib.rs +++ b/crates/cairo-coverage-core/src/lib.rs @@ -10,7 +10,7 @@ use crate::data_loader::LoadedDataMap; use crate::input::{InputData, StatementCategoryFilter}; use crate::output::lcov::LcovFormat; use anyhow::{bail, ensure, Context, Result}; -use cairo_coverage_args::CairoCoverageArgs; +use cairo_coverage_args::run::RunArgs; use camino::Utf8PathBuf; use indoc::indoc; use merge::MergeOwned; @@ -25,12 +25,12 @@ const SNFORGE_SIERRA_DIR: &str = ".snfoundry_versioned_programs"; /// # Errors /// Fails if it can't produce the coverage report with the error message explaining the reason. pub fn run( - CairoCoverageArgs { + RunArgs { trace_files, include, output_path, project_path, - }: CairoCoverageArgs, + }: RunArgs, ) -> Result<()> { let coverage_data = LoadedDataMap::load(&trace_files)? .iter() diff --git a/crates/cairo-coverage/src/commands/mod.rs b/crates/cairo-coverage/src/commands/mod.rs new file mode 100644 index 0000000..4ed54ca --- /dev/null +++ b/crates/cairo-coverage/src/commands/mod.rs @@ -0,0 +1,11 @@ +mod run; + +use anyhow::Result; +use cairo_coverage_args::Command; + +/// Run chosen [`Command`]. +pub fn run(command: Command) -> Result<()> { + match command { + Command::Run(args) => run::run(args), + } +} diff --git a/crates/cairo-coverage/src/commands/run.rs b/crates/cairo-coverage/src/commands/run.rs new file mode 100644 index 0000000..b167255 --- /dev/null +++ b/crates/cairo-coverage/src/commands/run.rs @@ -0,0 +1,8 @@ +use anyhow::Result; +use cairo_coverage_args::run::RunArgs; + +/// Run the `cairo-coverage run` command with [`RunArgs`]. +/// This is done by calling the [`cairo_coverage_core`] crate. +pub fn run(args: RunArgs) -> Result<()> { + cairo_coverage_core::run(args) +} diff --git a/crates/cairo-coverage/src/main.rs b/crates/cairo-coverage/src/main.rs index e747370..324d0bd 100644 --- a/crates/cairo-coverage/src/main.rs +++ b/crates/cairo-coverage/src/main.rs @@ -1,9 +1,19 @@ use anyhow::Result; -use cairo_coverage_args::CairoCoverageArgs; +use cairo_coverage_args::{CairoCoverageArgs, Command}; use clap::Parser; +mod commands; + fn main() -> Result<()> { let cairo_coverage_args = CairoCoverageArgs::parse(); - cairo_coverage_core::run(cairo_coverage_args) + let command = match cairo_coverage_args.command { + Some(command) => command, + // TODO: + // * In 0.5.0 add deprecation warning + // * In 0.6.0 remove the default command + None => Command::Run(cairo_coverage_args.run_args), + }; + + commands::run(command) }