use crate::cli::args::{ComplyCommands, ComplyFormat, ComplyScopeArg, ComplyTrackCommands};
use crate::models::{Error, Result};
use std::path::Path;
use tracing::info;
pub(crate) fn handle_comply_command(command: ComplyCommands) -> Result<()> {
match command {
ComplyCommands::Init {
scope,
pzsh,
strict,
} => comply_init_command(scope, pzsh, strict),
ComplyCommands::Check {
path,
scope,
strict,
failures_only,
min_score,
format,
} => comply_check_command(&path, scope, strict, failures_only, min_score, format),
ComplyCommands::Status { path, format } => comply_status_command(&path, format),
ComplyCommands::Track { command } => handle_comply_track_command(command),
ComplyCommands::Rules { format } => comply_rules_command(format),
ComplyCommands::Report {
path,
format,
output,
scope,
} => comply_report_command(&path, format, output.as_deref(), scope),
ComplyCommands::Enforce { tier, uninstall } => comply_enforce_command(tier, uninstall),
ComplyCommands::Diff { path, since_last } => comply_diff_command(&path, since_last),
}
}
fn comply_load_or_default(path: &Path) -> crate::comply::config::ComplyConfig {
use crate::comply::config::ComplyConfig;
let version = env!("CARGO_PKG_VERSION");
if ComplyConfig::exists(path) {
ComplyConfig::load(path).unwrap_or_else(|| ComplyConfig::new_default(version))
} else {
ComplyConfig::new_default(version)
}
}
fn comply_scope_filter(scope: Option<ComplyScopeArg>) -> Option<crate::comply::config::Scope> {
scope.and_then(|s| match s {
ComplyScopeArg::Project => Some(crate::comply::config::Scope::Project),
ComplyScopeArg::User => Some(crate::comply::config::Scope::User),
ComplyScopeArg::System => Some(crate::comply::config::Scope::System),
ComplyScopeArg::All => None,
})
}
fn comply_init_command(scope: ComplyScopeArg, pzsh: bool, strict: bool) -> Result<()> {
use crate::comply::config::ComplyConfig;
info!("Initializing comply manifest");
if ComplyConfig::exists(Path::new(".")) {
return Err(Error::Validation(
".bashrs/comply.toml already exists. Delete it first to reinitialize.".into(),
));
}
let mut config = ComplyConfig::new_default(env!("CARGO_PKG_VERSION"));
apply_comply_scope(&mut config, scope);
if pzsh {
config.integration.pzsh = "enabled".to_string();
}
if strict {
apply_comply_strict(&mut config);
}
config
.save(Path::new("."))
.map_err(|e| Error::Internal(format!("Failed to save comply.toml: {e}")))?;
println!("Initialized .bashrs/comply.toml");
println!(
" Scopes: project={} user={} system={}",
config.scopes.project, config.scopes.user, config.scopes.system
);
if pzsh {
println!(" pzsh integration: enabled");
}
if strict {
println!(" Mode: strict (all rules enforced)");
}
Ok(())
}
fn apply_comply_scope(config: &mut crate::comply::config::ComplyConfig, scope: ComplyScopeArg) {
match scope {
ComplyScopeArg::Project => {
config.scopes.user = false;
config.scopes.system = false;
}
ComplyScopeArg::User => {
config.scopes.user = true;
config.scopes.system = false;
}
ComplyScopeArg::System => {
config.scopes.user = false;
config.scopes.system = true;
}
ComplyScopeArg::All => {
config.scopes.user = true;
config.scopes.system = true;
}
}
}
fn apply_comply_strict(config: &mut crate::comply::config::ComplyConfig) {
config.rules.posix = true;
config.rules.determinism = true;
config.rules.idempotency = true;
config.rules.security = true;
config.rules.quoting = true;
config.rules.shellcheck = true;
config.rules.makefile_safety = true;
config.rules.dockerfile_best = true;
config.rules.config_hygiene = true;
config.rules.pzsh_budget = "10ms".to_string();
}
fn comply_check_command(
path: &Path,
scope: Option<ComplyScopeArg>,
strict: bool,
failures_only: bool,
min_score: Option<u32>,
format: ComplyFormat,
) -> Result<()> {
use crate::comply::{runner, scoring::Grade};
info!("Running compliance check on {}", path.display());
let has_config = crate::comply::config::ComplyConfig::exists(path);
let config = comply_load_or_default(path);
let score = runner::run_check(path, comply_scope_filter(scope), &config);
comply_output_score(&score, format, failures_only);
if strict && score.grade == Grade::F {
return Err(Error::Validation(format!(
"Compliance check failed: grade {} (score {:.0}/100)",
score.grade, score.score
)));
}
if let Some(min) = min_score {
if score.score < f64::from(min) {
return Err(Error::Validation(format!(
"Score {:.0} below minimum {} (grade {})",
score.score, min, score.grade
)));
}
}
if has_config {
comply_enforce_thresholds(&score, &config)?;
}
Ok(())
}
fn comply_enforce_thresholds(
score: &crate::comply::scoring::ProjectScore,
config: &crate::comply::config::ComplyConfig,
) -> Result<()> {
use crate::comply::scoring::Grade;
if config.thresholds.min_score > 0 && score.score < f64::from(config.thresholds.min_score) {
return Err(Error::Validation(format!(
"Score {:.0} below config threshold {} (grade {})",
score.score, config.thresholds.min_score, score.grade
)));
}
if config.thresholds.max_violations > 0 {
let total_violations: usize = score.artifact_scores.iter().map(|a| a.violations).sum();
if total_violations > config.thresholds.max_violations as usize {
return Err(Error::Validation(format!(
"Violations {} exceed config max {} (grade {})",
total_violations, config.thresholds.max_violations, score.grade
)));
}
}
let _ = Grade::F;
Ok(())
}
fn comply_status_command(path: &Path, format: ComplyFormat) -> Result<()> {
use crate::comply::runner;
info!("Checking compliance status for {}", path.display());
let config = comply_load_or_default(path);
let score = runner::run_check(path, None, &config);
comply_output_score(&score, format, false);
Ok(())
}
fn comply_rules_command(format: ComplyFormat) -> Result<()> {
use crate::comply::rules::RuleId;
match format {
ComplyFormat::Text => {
println!("═══════════════════════════════════════════════════════════");
println!(" COMPLIANCE RULES — 10 Falsifiable Hypotheses");
println!("═══════════════════════════════════════════════════════════\n");
println!(" {:<12} {:<22} {:>6} Applies To", "Code", "Name", "Weight");
println!("{}", "─".repeat(70));
for rule in RuleId::all() {
println!(
" {:<12} {:<22} {:>4} {}",
rule.code(),
rule.name(),
rule.weight(),
rule.applies_to().join(", ")
);
println!(" {}", rule.description());
println!();
}
let total_weight: u32 = RuleId::all().iter().map(|r| r.weight()).sum();
println!("{}", "─".repeat(70));
println!(
" {} rules | total weight: {} | suppress: # comply:disable=COMPLY-NNN",
RuleId::all().len(),
total_weight
);
println!("═══════════════════════════════════════════════════════════");
}
ComplyFormat::Json => {
let rules: Vec<String> = RuleId::all()
.iter()
.map(|r| {
format!(
r#" {{"code":"{}","name":"{}","weight":{},"applies_to":[{}],"description":"{}"}}"#,
r.code(),
r.name(),
r.weight(),
r.applies_to()
.iter()
.map(|s| format!("\"{}\"", s))
.collect::<Vec<_>>()
.join(","),
r.description()
)
})
.collect();
println!(
"{{\"schema\":\"bashrs-comply-rules-v1\",\"rules\":[\n{}\n]}}",
rules.join(",\n")
);
}
ComplyFormat::Markdown => {
println!("# Compliance Rules\n");
println!("| Code | Name | Weight | Applies To | Description |");
println!("|------|------|--------|------------|-------------|");
for rule in RuleId::all() {
println!(
"| {} | {} | {} | {} | {} |",
rule.code(),
rule.name(),
rule.weight(),
rule.applies_to().join(", "),
rule.description()
);
}
println!("\n*Suppress with: `# comply:disable=COMPLY-NNN`*");
}
}
Ok(())
}
fn comply_output_score(
score: &crate::comply::scoring::ProjectScore,
format: ComplyFormat,
failures_only: bool,
) {
use crate::comply::runner;
match format {
ComplyFormat::Text => {
if failures_only {
print!("{}", runner::format_human_failures_only(score));
} else {
print!("{}", runner::format_human(score));
}
}
ComplyFormat::Json => println!("{}", runner::format_json(score)),
ComplyFormat::Markdown => {
println!("# Compliance Report\n");
println!("**Score**: {:.0}/100 ({})\n", score.score, score.grade);
println!("| Artifact | Score | Grade | Status |");
println!("|----------|-------|-------|--------|");
for a in &score.artifact_scores {
if failures_only && a.violations == 0 {
continue;
}
let status = if a.violations == 0 {
"COMPLIANT"
} else {
"NON-COMPLIANT"
};
println!(
"| {} | {:.0} | {} | {} |",
a.artifact_name, a.score, a.grade, status
);
}
}
}
}
fn handle_comply_track_command(command: ComplyTrackCommands) -> Result<()> {
match command {
ComplyTrackCommands::Discover { path, scope } => comply_track_discover(&path, scope),
ComplyTrackCommands::List { path, scope } => comply_track_list(&path, scope),
}
}
fn comply_track_discover(path: &Path, scope: ComplyScopeArg) -> Result<()> {
use crate::comply::discovery;
info!("Discovering artifacts in {}", path.display());
if matches!(scope, ComplyScopeArg::All) {
return comply_track_discover_all(path);
}
let scope_val = comply_scope_to_internal(scope);
let artifacts = discovery::discover(path, scope_val);
comply_print_artifact_list(scope_val, &artifacts);
Ok(())
}
fn comply_track_discover_all(path: &Path) -> Result<()> {
use crate::comply::{config::Scope, discovery};
let mut total = 0;
for s in &[Scope::Project, Scope::User, Scope::System] {
let artifacts = discovery::discover(path, *s);
if !artifacts.is_empty() {
println!("{:?} scope ({} artifacts):", s, artifacts.len());
for a in &artifacts {
println!(" {} [{:?}]", a.display_name(), a.kind);
}
total += artifacts.len();
}
}
println!("\nTotal: {} artifacts discovered", total);
Ok(())
}
include!("comply_commands_comply.rs");