#![cfg_attr(coverage_nightly, coverage(off))]
use super::hooks_command::HooksCommand;
use anyhow::Result;
use std::fs;
impl HooksCommand {
pub(super) fn run_interactive_setup(&self) -> Result<()> {
println!("🔧 Interactive Pre-commit Hook Setup");
println!("====================================\n");
let project_type = self.detect_project_type();
println!("📦 Detected project type: {project_type}");
println!("\n⚙️ Quality Thresholds:");
let max_complexity =
self.prompt_number("Maximum cyclomatic complexity (default: 10)", 10)?;
let max_cognitive = self.prompt_number("Maximum cognitive complexity (default: 15)", 15)?;
let min_coverage = self.prompt_number("Minimum test coverage % (default: 80)", 80)?;
let max_satd = self.prompt_number("Maximum SATD comments (default: 5)", 5)?;
println!("\n📝 Updating configuration...");
let config_path = std::env::current_dir()?.join("pmat.toml");
if config_path.exists() {
let config_content = fs::read_to_string(&config_path)?;
let updated = self.update_config_values(
&config_content,
max_complexity,
max_cognitive,
min_coverage,
max_satd,
);
fs::write(&config_path, updated)?;
println!("✅ Updated pmat.toml with your preferences");
} else {
let config_content =
self.generate_config_content(max_complexity, max_cognitive, min_coverage, max_satd);
fs::write(&config_path, config_content)?;
println!("✅ Created pmat.toml with your preferences");
}
println!("\n✅ Interactive setup complete!\n");
Ok(())
}
fn prompt_number(&self, prompt: &str, default: u32) -> Result<u32> {
use std::io::{self, Write};
print!(" {prompt}: ");
io::stdout().flush()?;
let mut input = String::new();
io::stdin().read_line(&mut input)?;
let input = input.trim();
if input.is_empty() {
Ok(default)
} else {
input
.parse::<u32>()
.map_err(|e| anyhow::anyhow!("Invalid number: {}", e))
}
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub(crate) fn detect_project_type(&self) -> String {
let current_dir = std::env::current_dir().ok();
if let Some(dir) = current_dir {
if dir.join("Cargo.toml").exists() {
return "Rust".to_string();
}
if dir.join("package.json").exists() {
return "JavaScript/TypeScript".to_string();
}
if dir.join("pyproject.toml").exists() || dir.join("setup.py").exists() {
return "Python".to_string();
}
if dir.join("go.mod").exists() {
return "Go".to_string();
}
}
"Unknown".to_string()
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub(crate) fn update_config_values(
&self,
content: &str,
max_complexity: u32,
max_cognitive: u32,
min_coverage: u32,
_max_satd: u32,
) -> String {
let old_complexity = self.extract_current_value(content, "max_complexity");
let content = content.replace(
&format!("max_complexity = {old_complexity}"),
&format!("max_complexity = {max_complexity}"),
);
let old_cognitive = self.extract_current_value(&content, "max_cognitive_complexity");
let content = content.replace(
&format!("max_cognitive_complexity = {old_cognitive}"),
&format!("max_cognitive_complexity = {max_cognitive}"),
);
let old_coverage = self.extract_current_value(&content, "min_coverage");
content.replace(
&format!("min_coverage = {old_coverage}"),
&format!("min_coverage = {min_coverage}"),
)
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub(crate) fn extract_current_value(&self, content: &str, key: &str) -> String {
content
.lines()
.find(|line| line.contains(key))
.and_then(|line| line.split('=').nth(1))
.map(|val| val.trim().to_string())
.unwrap_or_else(|| "10".to_string())
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub(crate) fn generate_config_content(
&self,
max_complexity: u32,
max_cognitive: u32,
min_coverage: u32,
max_satd: u32,
) -> String {
format!(
r#"# PMAT Configuration File
# Generated by interactive setup
[quality]
max_complexity = {max_complexity}
max_cognitive_complexity = {max_cognitive}
min_coverage = {min_coverage}
max_satd_comments = {max_satd}
min_grade = "B+"
[hooks]
enabled = true
fail_on_warning = false
show_diff = true
auto_fix = false
[hooks.performance]
timeout = 30
max_files = 1000
incremental = true
"#
)
}
}