#![cfg_attr(coverage_nightly, coverage(off))]
use super::{
AnalyzeComplexityContract, AnalyzeDeadCodeContract, AnalyzeLintHotspotContract,
AnalyzeSatdContract, AnalyzeTdgContract, BaseAnalysisContract, OutputFormat, SatdSeverity,
};
use clap::{Parser, Subcommand, ValueEnum};
use std::path::PathBuf;
#[derive(Subcommand)]
#[cfg_attr(test, derive(Debug))]
pub enum UniformAnalyzeCommands {
#[command(name = "complexity")]
Complexity(UniformComplexityArgs),
#[command(name = "satd")]
Satd(UniformSatdArgs),
#[command(name = "dead-code")]
DeadCode(UniformDeadCodeArgs),
#[command(name = "tdg")]
Tdg(UniformTdgArgs),
#[command(name = "lint-hotspot")]
LintHotspot(UniformLintHotspotArgs),
}
#[derive(Parser)]
#[cfg_attr(test, derive(Debug))]
pub struct UniformComplexityArgs {
#[arg(short = 'p', long, default_value = ".")]
pub path: PathBuf,
#[arg(long, value_enum, default_value = "table")]
pub format: UniformOutputFormat,
#[arg(short, long)]
pub output: Option<PathBuf>,
#[arg(long, default_value_t = 10)]
pub top_files: usize,
#[arg(long)]
pub include_tests: bool,
#[arg(long, default_value_t = 60)]
pub timeout: u64,
#[arg(long)]
pub max_cyclomatic: Option<u32>,
#[arg(long)]
pub max_cognitive: Option<u32>,
#[arg(long)]
pub max_halstead: Option<f64>,
}
#[derive(Parser)]
#[cfg_attr(test, derive(Debug))]
pub struct UniformSatdArgs {
#[arg(short = 'p', long, default_value = ".")]
pub path: PathBuf,
#[arg(long, value_enum, default_value = "table")]
pub format: UniformOutputFormat,
#[arg(short, long)]
pub output: Option<PathBuf>,
#[arg(long, default_value_t = 10)]
pub top_files: usize,
#[arg(long)]
pub include_tests: bool,
#[arg(long, default_value_t = 60)]
pub timeout: u64,
#[arg(long, value_enum)]
pub severity: Option<UniformSatdSeverity>,
#[arg(long)]
pub critical_only: bool,
#[arg(long)]
pub strict: bool,
#[arg(long)]
pub fail_on_violation: bool,
}
#[derive(Parser)]
#[cfg_attr(test, derive(Debug))]
pub struct UniformDeadCodeArgs {
#[arg(short = 'p', long, default_value = ".")]
pub path: PathBuf,
#[arg(long, value_enum, default_value = "table")]
pub format: UniformOutputFormat,
#[arg(short, long)]
pub output: Option<PathBuf>,
#[arg(long, default_value_t = 10)]
pub top_files: usize,
#[arg(long)]
pub include_tests: bool,
#[arg(long, default_value_t = 60)]
pub timeout: u64,
#[arg(long)]
pub include_unreachable: bool,
#[arg(long, default_value_t = 10)]
pub min_dead_lines: usize,
#[arg(long, default_value_t = 15.0)]
pub max_percentage: f64,
#[arg(long)]
pub fail_on_violation: bool,
}
#[derive(Parser)]
#[cfg_attr(test, derive(Debug))]
pub struct UniformTdgArgs {
#[arg(short = 'p', long, default_value = ".")]
pub path: PathBuf,
#[arg(long, value_enum, default_value = "table")]
pub format: UniformOutputFormat,
#[arg(short, long)]
pub output: Option<PathBuf>,
#[arg(long, default_value_t = 10)]
pub top_files: usize,
#[arg(long)]
pub include_tests: bool,
#[arg(long, default_value_t = 60)]
pub timeout: u64,
#[arg(long, default_value_t = 1.5)]
pub threshold: f64,
#[arg(long)]
pub include_components: bool,
#[arg(long)]
pub critical_only: bool,
}
#[derive(Parser)]
#[cfg_attr(test, derive(Debug))]
pub struct UniformLintHotspotArgs {
#[arg(short = 'p', long, default_value = ".")]
pub path: PathBuf,
#[arg(long, value_enum, default_value = "table")]
pub format: UniformOutputFormat,
#[arg(short, long)]
pub output: Option<PathBuf>,
#[arg(long, default_value_t = 10)]
pub top_files: usize,
#[arg(long)]
pub include_tests: bool,
#[arg(long, default_value_t = 60)]
pub timeout: u64,
#[arg(long)]
pub file: Option<PathBuf>,
#[arg(long, default_value_t = 5.0)]
pub max_density: f64,
#[arg(long, default_value_t = 0.8)]
pub min_confidence: f64,
#[arg(long)]
pub enforce: bool,
#[arg(long)]
pub dry_run: bool,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
pub enum UniformOutputFormat {
Table,
Json,
Yaml,
Markdown,
Csv,
Summary,
}
impl From<UniformOutputFormat> for OutputFormat {
fn from(format: UniformOutputFormat) -> Self {
match format {
UniformOutputFormat::Table => OutputFormat::Table,
UniformOutputFormat::Json => OutputFormat::Json,
UniformOutputFormat::Yaml => OutputFormat::Yaml,
UniformOutputFormat::Markdown => OutputFormat::Markdown,
UniformOutputFormat::Csv => OutputFormat::Csv,
UniformOutputFormat::Summary => OutputFormat::Summary,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
pub enum UniformSatdSeverity {
Low,
Medium,
High,
Critical,
}
impl From<UniformSatdSeverity> for SatdSeverity {
fn from(severity: UniformSatdSeverity) -> Self {
match severity {
UniformSatdSeverity::Low => SatdSeverity::Low,
UniformSatdSeverity::Medium => SatdSeverity::Medium,
UniformSatdSeverity::High => SatdSeverity::High,
UniformSatdSeverity::Critical => SatdSeverity::Critical,
}
}
}
impl From<UniformComplexityArgs> for AnalyzeComplexityContract {
fn from(args: UniformComplexityArgs) -> Self {
Self {
base: BaseAnalysisContract {
path: args.path,
format: args.format.into(),
output: args.output,
top_files: Some(args.top_files),
include_tests: args.include_tests,
timeout: args.timeout,
},
max_cyclomatic: args.max_cyclomatic,
max_cognitive: args.max_cognitive,
max_halstead: args.max_halstead,
}
}
}
impl From<UniformSatdArgs> for AnalyzeSatdContract {
fn from(args: UniformSatdArgs) -> Self {
Self {
base: BaseAnalysisContract {
path: args.path,
format: args.format.into(),
output: args.output,
top_files: Some(args.top_files),
include_tests: args.include_tests,
timeout: args.timeout,
},
severity: args.severity.map(std::convert::Into::into),
critical_only: args.critical_only,
strict: args.strict,
fail_on_violation: args.fail_on_violation,
}
}
}
impl From<UniformDeadCodeArgs> for AnalyzeDeadCodeContract {
fn from(args: UniformDeadCodeArgs) -> Self {
Self {
base: BaseAnalysisContract {
path: args.path,
format: args.format.into(),
output: args.output,
top_files: Some(args.top_files),
include_tests: args.include_tests,
timeout: args.timeout,
},
include_unreachable: args.include_unreachable,
min_dead_lines: args.min_dead_lines,
max_percentage: args.max_percentage,
fail_on_violation: args.fail_on_violation,
}
}
}
impl From<UniformTdgArgs> for AnalyzeTdgContract {
fn from(args: UniformTdgArgs) -> Self {
Self {
base: BaseAnalysisContract {
path: args.path,
format: args.format.into(),
output: args.output,
top_files: Some(args.top_files),
include_tests: args.include_tests,
timeout: args.timeout,
},
threshold: args.threshold,
include_components: args.include_components,
critical_only: args.critical_only,
}
}
}
impl From<UniformLintHotspotArgs> for AnalyzeLintHotspotContract {
fn from(args: UniformLintHotspotArgs) -> Self {
Self {
base: BaseAnalysisContract {
path: args.path,
format: args.format.into(),
output: args.output,
top_files: Some(args.top_files),
include_tests: args.include_tests,
timeout: args.timeout,
},
file: args.file,
max_density: args.max_density,
min_confidence: args.min_confidence,
enforce: args.enforce,
dry_run: args.dry_run,
}
}
}
pub struct UniformCommandHandler {
service: Arc<crate::contracts::service::ContractService>,
}
impl UniformCommandHandler {
pub fn new() -> anyhow::Result<Self> {
Ok(Self {
service: Arc::new(crate::contracts::service::ContractService::new()?),
})
}
pub async fn handle_analyze_command(&self, cmd: UniformAnalyzeCommands) -> anyhow::Result<()> {
match cmd {
UniformAnalyzeCommands::Complexity(args) => self.handle_complexity_analysis(args).await,
UniformAnalyzeCommands::Satd(args) => self.handle_satd_analysis(args).await,
UniformAnalyzeCommands::DeadCode(args) => self.handle_dead_code_analysis(args).await,
UniformAnalyzeCommands::Tdg(args) => self.handle_tdg_analysis(args).await,
UniformAnalyzeCommands::LintHotspot(args) => {
self.handle_lint_hotspot_analysis(args).await
}
}
}
async fn handle_complexity_analysis(&self, args: UniformComplexityArgs) -> anyhow::Result<()> {
let contract = AnalyzeComplexityContract::from(args);
let result = self.service.analyze_complexity(contract).await?;
self.output_result(result)
}
async fn handle_satd_analysis(&self, args: UniformSatdArgs) -> anyhow::Result<()> {
let contract = AnalyzeSatdContract::from(args);
let result = self.service.analyze_satd(contract).await?;
self.output_result(result)
}
async fn handle_dead_code_analysis(&self, args: UniformDeadCodeArgs) -> anyhow::Result<()> {
let contract = AnalyzeDeadCodeContract::from(args);
let result = self.service.analyze_dead_code(contract).await?;
self.output_result(result)
}
async fn handle_tdg_analysis(&self, args: UniformTdgArgs) -> anyhow::Result<()> {
let contract = AnalyzeTdgContract::from(args);
let result = self.service.analyze_tdg(contract).await?;
self.output_result(result)
}
async fn handle_lint_hotspot_analysis(
&self,
args: UniformLintHotspotArgs,
) -> anyhow::Result<()> {
let contract = AnalyzeLintHotspotContract::from(args);
let result = self.service.analyze_lint_hotspot(contract).await?;
self.output_result(result)
}
fn output_result(&self, result: serde_json::Value) -> anyhow::Result<()> {
match result {
serde_json::Value::String(s) => println!("{s}"),
other => println!("{}", serde_json::to_string_pretty(&other)?),
}
Ok(())
}
}
use std::sync::Arc;
include!("uniform_cli_commands_tests.rs");