diff --git a/Cargo.lock b/Cargo.lock index 2898374..06e704c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -225,6 +225,7 @@ dependencies = [ "cairo-coverage-test-utils", "camino", "clap", + "console", "snapbox", "walkdir", "which", @@ -507,6 +508,19 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +[[package]] +name = "console" +version = "0.15.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "unicode-width", + "windows-sys", +] + [[package]] name = "const-fnv1a-hash" version = "1.1.0" @@ -724,6 +738,12 @@ dependencies = [ "log", ] +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + [[package]] name = "env_home" version = "0.1.0" @@ -1986,6 +2006,12 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + [[package]] name = "unicode-xid" version = "0.2.6" diff --git a/Cargo.toml b/Cargo.toml index a75eb7e..6b04242 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,15 +11,17 @@ version = "0.4.0" edition = "2024" [workspace.dependencies] +cairo-lang-sierra = "2.10.1" +cairo-lang-sierra-to-casm = "2.10.1" +cairo-lang-starknet-classes = "2.10.1" +cairo-annotations = "0.2.2" + anyhow = "1.0.96" assert_fs = "1.1.2" camino = "1.1.9" clap = { version = "4.5.30", features = ["derive"] } criterion = "0.5" -cairo-annotations = "0.2.2" -cairo-lang-sierra = "2.10.1" -cairo-lang-sierra-to-casm = "2.10.1" -cairo-lang-starknet-classes = "2.10.1" +console = "0.15.10" itertools = "0.14.0" ignore = "0.4.23" serde = "1.0.218" diff --git a/crates/cairo-coverage/Cargo.toml b/crates/cairo-coverage/Cargo.toml index fc5b454..ad8057b 100644 --- a/crates/cairo-coverage/Cargo.toml +++ b/crates/cairo-coverage/Cargo.toml @@ -5,6 +5,7 @@ edition.workspace = true [dependencies] cairo-coverage-core = { path = "../cairo-coverage-core" } +console.workspace = true camino.workspace = true anyhow.workspace = true clap.workspace = true diff --git a/crates/cairo-coverage/src/args/run.rs b/crates/cairo-coverage/src/args/run.rs index af3999c..fd01732 100644 --- a/crates/cairo-coverage/src/args/run.rs +++ b/crates/cairo-coverage/src/args/run.rs @@ -34,11 +34,11 @@ pub enum IncludedComponent { 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!(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" + "trace file must have a JSON extension" ); Ok(trace_file) @@ -47,8 +47,8 @@ fn parse_trace_file(path: &str) -> Result { 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"); + 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/src/commands/clean.rs b/crates/cairo-coverage/src/commands/clean.rs index d8ea192..c65b2b4 100644 --- a/crates/cairo-coverage/src/commands/clean.rs +++ b/crates/cairo-coverage/src/commands/clean.rs @@ -1,4 +1,5 @@ use crate::args::clean::CleanArgs; +use crate::ui; use anyhow::{Context, Result}; use std::fs; use walkdir::WalkDir; @@ -13,7 +14,7 @@ pub fn run( ) -> Result<()> { let target_file_name = files_to_delete .file_name() - .context("Failed to obtain the file name from `files_to_delete`.")?; + .context("failed to obtain the file name from `--files-to-delete`")?; WalkDir::new(root_dir) .into_iter() @@ -24,16 +25,17 @@ pub fn run( if let Some(file_name) = path.file_name() { if file_name == target_file_name { - println!("Deleting file: {}", path.display()); + let path_display = path.display(); + ui::msg(format!("deleting file: {path_display}")); fs::remove_file(path) - .with_context(|| format!("Failed to delete file: {}", path.display()))?; + .with_context(|| format!("failed to delete file: {path_display}"))?; } } Ok(()) })?; - println!("Cleanup complete."); + ui::msg("cleanup complete"); Ok(()) } diff --git a/crates/cairo-coverage/src/commands/run.rs b/crates/cairo-coverage/src/commands/run.rs index 1246c34..34f0640 100644 --- a/crates/cairo-coverage/src/commands/run.rs +++ b/crates/cairo-coverage/src/commands/run.rs @@ -25,9 +25,9 @@ pub fn run( .append(true) .create(true) .open(&output_path) - .context(format!("Failed to open output file at path: {output_path}"))? + .context(format!("failed to open output file at path: {output_path}"))? .write_all(lcov.as_bytes()) - .context("Failed to write to output file")?; + .context("failed to write to output file")?; Ok(()) } diff --git a/crates/cairo-coverage/src/main.rs b/crates/cairo-coverage/src/main.rs index 2b2894f..9e07d2f 100644 --- a/crates/cairo-coverage/src/main.rs +++ b/crates/cairo-coverage/src/main.rs @@ -1,21 +1,36 @@ use crate::args::{CairoCoverageArgs, Command}; use anyhow::Result; + use clap::Parser; +use std::process::ExitCode; mod args; mod commands; +mod ui; + +fn main() -> ExitCode { + if let Err(error) = main_inner() { + ui::error(error); + ExitCode::FAILURE + } else { + ExitCode::SUCCESS + } +} -fn main() -> Result<()> { +fn main_inner() -> Result<()> { let cairo_coverage_args = CairoCoverageArgs::parse(); - 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.unwrap_or_else(|| { + // TODO: + // * In 0.6.0 remove the default command + let command = if let Some(command) = cairo_coverage_args.command { + command + } else { + ui::warning("running `cairo-coverage` without a subcommand is deprecated"); + ui::help("consider using `cairo-coverage run`"); + + Command::Run(cairo_coverage_args.run_args.unwrap_or_else(|| { unreachable!("`run_args` should be set when no subcommand is provided") - })), + })) }; commands::run(command) diff --git a/crates/cairo-coverage/src/ui.rs b/crates/cairo-coverage/src/ui.rs new file mode 100644 index 0000000..a9e157f --- /dev/null +++ b/crates/cairo-coverage/src/ui.rs @@ -0,0 +1,28 @@ +//! UI utilities for the Cairo coverage tool. +//! All human-oriented messaging must use this module to communicate with the user. +//! Messages should be lowercased and should not end with a period. +use console::style; +use std::fmt::Display; + +/// Print a warning message. +pub fn warning(message: impl Display) { + let tag = style("warning").yellow(); + println!("{tag}: {message}"); +} + +/// Print an error message. +pub fn error(message: impl Display) { + let tag = style("error").red(); + println!("{tag}: {message}"); +} + +/// Print a help message. +pub fn help(message: impl Display) { + let tag = style("help").bold(); + println!("{tag}: {message}"); +} + +/// Print a message. +pub fn msg(message: impl Display) { + println!("{message}"); +}