use crate::services::configuration_service::{configuration, ConfigurationService, PmatConfig};
use anyhow::Result;
use std::path::PathBuf;
use tracing::info;
pub async fn handle_configuration(
show: bool,
edit: bool,
validate: bool,
reset: bool,
section: Option<String>,
set: Vec<String>,
config_path: Option<PathBuf>,
) -> Result<()> {
let config_service = create_config_service(config_path);
execute_configuration_command(
&config_service,
ConfigurationCommand {
show,
edit,
validate,
reset,
section,
set,
},
)
.await
}
struct ConfigurationCommand {
show: bool,
edit: bool,
validate: bool,
reset: bool,
section: Option<String>,
set: Vec<String>,
}
fn create_config_service(config_path: Option<PathBuf>) -> std::sync::Arc<ConfigurationService> {
if let Some(path) = config_path {
std::sync::Arc::new(ConfigurationService::new(Some(path)))
} else {
configuration()
}
}
async fn execute_configuration_command(
config_service: &ConfigurationService,
cmd: ConfigurationCommand,
) -> Result<()> {
if cmd.reset {
return execute_reset_command(config_service).await;
}
if cmd.validate {
return validate_configuration(config_service).await;
}
if !cmd.set.is_empty() {
return set_configuration_values(config_service, cmd.set).await;
}
if cmd.edit {
return edit_configuration(config_service).await;
}
execute_show_command(config_service, cmd.show, cmd.section).await
}
async fn execute_reset_command(config_service: &ConfigurationService) -> Result<()> {
reset_configuration(config_service).await?;
println!("✅ Configuration reset to defaults");
Ok(())
}
async fn execute_show_command(
config_service: &ConfigurationService,
show: bool,
section: Option<String>,
) -> Result<()> {
if show || section.is_some() {
show_configuration(config_service, section).await
} else {
show_configuration_overview(config_service).await
}
}
async fn show_configuration_overview(config_service: &ConfigurationService) -> Result<()> {
info!("📊 Generating configuration overview");
let config = config_service.get_config()?;
println!("🔧 PMAT Configuration Overview");
println!("{}", "=".repeat(35));
println!();
println!("📍 Configuration Source:");
let config_path = std::env::current_dir()?.join("pmat.toml");
if config_path.exists() {
println!(" File: {} ✅", config_path.display());
} else {
println!(" File: {} (default)", config_path.display());
}
println!();
println!("🎯 System Settings:");
println!(" Project: {}", config.system.project_name);
println!(" Toolchain: {}", config.system.default_toolchain);
println!(
" Parallel: {} threads",
config.system.max_concurrent_operations
);
println!();
println!("🔍 Quality Gates:");
println!(" Max Complexity: {}", config.quality.max_complexity);
println!(" Min Coverage: {}%", config.quality.min_coverage);
println!(" Allow SATD: {}", config.quality.allow_satd);
println!(" Require Docs: {}", config.quality.require_docs);
println!();
println!("📊 Analysis Settings:");
println!(" Include: {:?}", config.analysis.include_patterns);
println!(" Exclude: {:?}", config.analysis.exclude_patterns);
println!(" Parallel: {}", config.analysis.parallel);
println!(" Timeout: {}s", config.analysis.timeout_seconds);
println!();
println!("🚀 Performance Targets:");
println!(
" Startup: {}ms",
config.performance.target_startup_latency_ms
);
println!(
" Throughput: {} LOC/s",
config.performance.target_throughput_loc_per_sec
);
println!(" Memory: {}MB", config.performance.target_memory_mb);
println!();
println!("🔗 MCP Server:");
println!(" Name: {}", config.mcp.server_name);
println!(" Version: {}", config.mcp.server_version);
println!(" Tools: {} enabled", config.mcp.enabled_tools.len());
println!();
println!("📋 Roadmap:");
println!(" Path: {}", config.roadmap.roadmap_path.display());
println!(" Auto Todos: {}", config.roadmap.auto_generate_todos);
println!(" Quality Gates: {}", config.roadmap.enforce_quality_gates);
println!();
println!("📈 Telemetry:");
println!(" Enabled: {}", config.telemetry.enabled);
println!(
" Interval: {}s",
config.telemetry.collection_interval_seconds
);
println!();
println!("💡 Commands:");
println!(" pmat config --show --section quality # Show quality settings");
println!(" pmat config --set quality.max_complexity=25 # Update setting");
println!(" pmat config --edit # Interactive edit");
println!(" pmat config --validate # Validate config");
Ok(())
}
async fn show_configuration(
config_service: &ConfigurationService,
section: Option<String>,
) -> Result<()> {
let config = config_service.get_config()?;
if let Some(section_name) = section {
show_configuration_section(&config, §ion_name)?;
} else {
println!("🔧 Complete PMAT Configuration");
println!("{}", "=".repeat(50));
println!();
println!("📄 Raw Configuration (TOML):");
let toml_content = toml::to_string_pretty(&config)?;
println!("{toml_content}");
println!("📄 JSON Format:");
println!("{}", serde_json::to_string_pretty(&config)?);
}
Ok(())
}
fn show_configuration_section(config: &PmatConfig, section: &str) -> Result<()> {
println!("🔧 Configuration Section: {section}");
println!("{}", "=".repeat(30 + section.len()));
println!();
match section.to_lowercase().as_str() {
"system" => {
println!("📄 System Configuration (TOML):");
println!("{}", toml::to_string_pretty(&config.system)?);
}
"quality" => {
println!("📄 Quality Configuration (TOML):");
println!("{}", toml::to_string_pretty(&config.quality)?);
}
"analysis" => {
println!("📄 Analysis Configuration (TOML):");
println!("{}", toml::to_string_pretty(&config.analysis)?);
}
"performance" => {
println!("📄 Performance Configuration (TOML):");
println!("{}", toml::to_string_pretty(&config.performance)?);
}
"mcp" => {
println!("📄 MCP Configuration (TOML):");
println!("{}", toml::to_string_pretty(&config.mcp)?);
}
"roadmap" => {
println!("📄 Roadmap Configuration (TOML):");
println!("{}", toml::to_string_pretty(&config.roadmap)?);
}
"telemetry" => {
println!("📄 Telemetry Configuration (TOML):");
println!("{}", toml::to_string_pretty(&config.telemetry)?);
}
_ => {
return Err(anyhow::anyhow!(
"Unknown section '{section}'. Available: system, quality, analysis, performance, mcp, roadmap, telemetry"
));
}
}
Ok(())
}
async fn set_configuration_values(
config_service: &ConfigurationService,
set_values: Vec<String>,
) -> Result<()> {
info!("🔧 Updating configuration values");
for set_value in set_values {
let parts: Vec<&str> = set_value.splitn(2, '=').collect();
if parts.len() != 2 {
return Err(anyhow::anyhow!(
"Invalid format '{set_value}'. Use key=value"
));
}
let key = parts[0];
let value = parts[1];
println!("Setting {key}: {value}");
config_service
.update_config(|config| set_config_value(config, key, value))
.await?;
}
println!("✅ Configuration updated successfully");
Ok(())
}
fn set_config_value(config: &mut PmatConfig, key: &str, value: &str) -> Result<()> {
let parts: Vec<&str> = key.split('.').collect();
if parts.len() != 2 {
return Err(anyhow::anyhow!(
"Key must be in format 'section.field', got '{key}'"
));
}
let section = parts[0];
let field = parts[1];
match section {
"system" => set_system_value(&mut config.system, field, value),
"quality" => set_quality_value(&mut config.quality, field, value),
"analysis" => set_analysis_value(&mut config.analysis, field, value),
"performance" => set_performance_value(&mut config.performance, field, value),
"mcp" => set_mcp_value(&mut config.mcp, field, value),
"roadmap" => set_roadmap_value(&mut config.roadmap, field, value),
"telemetry" => set_telemetry_value(&mut config.telemetry, field, value),
_ => Err(anyhow::anyhow!("Unknown section '{section}'")),
}
}
fn set_system_value(
system: &mut crate::services::configuration_service::SystemConfig,
field: &str,
value: &str,
) -> Result<()> {
match field {
"project_name" => system.project_name = value.to_string(),
"verbose" => system.verbose = value.parse()?,
"debug" => system.debug = value.parse()?,
"default_toolchain" => system.default_toolchain = value.to_string(),
"max_concurrent_operations" => system.max_concurrent_operations = value.parse()?,
_ => return Err(anyhow::anyhow!("Unknown system field '{field}'")),
}
Ok(())
}
fn set_quality_value(
quality: &mut crate::services::configuration_service::QualityConfig,
field: &str,
value: &str,
) -> Result<()> {
match field {
"max_complexity" => quality.max_complexity = value.parse()?,
"max_cognitive_complexity" => quality.max_cognitive_complexity = value.parse()?,
"min_coverage" => quality.min_coverage = value.parse()?,
"allow_satd" => quality.allow_satd = value.parse()?,
"require_docs" => quality.require_docs = value.parse()?,
"lint_compliance" => quality.lint_compliance = value.parse()?,
"fail_on_violation" => quality.fail_on_violation = value.parse()?,
_ => return Err(anyhow::anyhow!("Unknown quality field '{field}'")),
}
Ok(())
}
fn set_analysis_value(
analysis: &mut crate::services::configuration_service::AnalysisConfig,
field: &str,
value: &str,
) -> Result<()> {
match field {
"max_file_size" => analysis.max_file_size = value.parse()?,
"max_line_length" => analysis.max_line_length = value.parse()?,
"skip_vendor" => analysis.skip_vendor = value.parse()?,
"parallel" => analysis.parallel = value.parse()?,
"thread_count" => analysis.thread_count = value.parse()?,
"timeout_seconds" => analysis.timeout_seconds = value.parse()?,
_ => return Err(anyhow::anyhow!("Unknown analysis field '{field}'")),
}
Ok(())
}
fn set_performance_value(
performance: &mut crate::services::configuration_service::PerformanceConfig,
field: &str,
value: &str,
) -> Result<()> {
match field {
"enable_regression_tests" => performance.enable_regression_tests = value.parse()?,
"enable_memory_tests" => performance.enable_memory_tests = value.parse()?,
"enable_throughput_tests" => performance.enable_throughput_tests = value.parse()?,
"test_iterations" => performance.test_iterations = value.parse()?,
"timeout_ms" => performance.timeout_ms = value.parse()?,
"target_startup_latency_ms" => performance.target_startup_latency_ms = value.parse()?,
"target_throughput_loc_per_sec" => {
performance.target_throughput_loc_per_sec = value.parse()?;
}
"target_memory_mb" => performance.target_memory_mb = value.parse()?,
_ => return Err(anyhow::anyhow!("Unknown performance field '{field}'")),
}
Ok(())
}
fn set_mcp_value(
mcp: &mut crate::services::configuration_service::McpConfig,
field: &str,
value: &str,
) -> Result<()> {
match field {
"server_name" => mcp.server_name = value.to_string(),
"server_version" => mcp.server_version = value.to_string(),
"enable_compression" => mcp.enable_compression = value.parse()?,
"request_timeout_seconds" => mcp.request_timeout_seconds = value.parse()?,
"max_request_size" => mcp.max_request_size = value.parse()?,
"log_requests" => mcp.log_requests = value.parse()?,
_ => return Err(anyhow::anyhow!("Unknown mcp field '{field}'")),
}
Ok(())
}
fn set_roadmap_value(
roadmap: &mut crate::services::configuration_service::RoadmapConfig,
field: &str,
value: &str,
) -> Result<()> {
match field {
"auto_generate_todos" => roadmap.auto_generate_todos = value.parse()?,
"enforce_quality_gates" => roadmap.enforce_quality_gates = value.parse()?,
"require_task_ids" => roadmap.require_task_ids = value.parse()?,
"task_id_pattern" => roadmap.task_id_pattern = value.to_string(),
"velocity_tracking" => roadmap.velocity_tracking = value.parse()?,
"burndown_charts" => roadmap.burndown_charts = value.parse()?,
_ => return Err(anyhow::anyhow!("Unknown roadmap field '{field}'")),
}
Ok(())
}
fn set_telemetry_value(
telemetry: &mut crate::services::configuration_service::TelemetryConfig,
field: &str,
value: &str,
) -> Result<()> {
match field {
"enabled" => telemetry.enabled = value.parse()?,
"collection_interval_seconds" => telemetry.collection_interval_seconds = value.parse()?,
"max_data_age_days" => telemetry.max_data_age_days = value.parse()?,
"enable_aggregation" => telemetry.enable_aggregation = value.parse()?,
"enable_export" => telemetry.enable_export = value.parse()?,
"export_format" => telemetry.export_format = value.to_string(),
_ => return Err(anyhow::anyhow!("Unknown telemetry field '{field}'")),
}
Ok(())
}
async fn edit_configuration(config_service: &ConfigurationService) -> Result<()> {
println!("🔧 Interactive Configuration Editor");
println!("{}", "=".repeat(40));
println!();
let config = config_service.get_config()?;
let toml_content = toml::to_string_pretty(&config)?;
let temp_dir = tempfile::tempdir()?;
let temp_file = temp_dir.path().join("pmat_config.toml");
tokio::fs::write(&temp_file, &toml_content).await?;
let editor = std::env::var("EDITOR")
.or_else(|_| std::env::var("VISUAL"))
.unwrap_or_else(|_| "nano".to_string());
println!("Opening configuration in {editor} editor...");
println!("Save and exit to apply changes, or exit without saving to cancel.");
println!();
let status = std::process::Command::new(&editor)
.arg(&temp_file)
.status()?;
if !status.success() {
return Err(anyhow::anyhow!("Editor exited with error"));
}
let edited_content = tokio::fs::read_to_string(&temp_file).await?;
let new_config: PmatConfig = toml::from_str(&edited_content)?;
config_service
.update_config(|config| {
*config = new_config;
Ok(())
})
.await?;
println!("✅ Configuration updated successfully");
Ok(())
}
async fn validate_configuration(config_service: &ConfigurationService) -> Result<()> {
info!("🔍 Validating configuration");
println!("🔍 PMAT Configuration Validation");
println!("{}", "=".repeat(40));
println!();
let config = config_service.get_config()?;
let mut issues = Vec::new();
validate_all_sections(&config, &mut issues);
report_validation_results(&config, &issues)?;
Ok(())
}
fn validate_all_sections(config: &PmatConfig, issues: &mut Vec<&'static str>) {
validate_system_config(&config.system, issues);
validate_quality_config(&config.quality, issues);
validate_analysis_config(&config.analysis, issues);
validate_performance_config(&config.performance, issues);
validate_mcp_config(&config.mcp, issues);
}
fn validate_system_config(
system_config: &crate::services::configuration_service::SystemConfig,
issues: &mut Vec<&'static str>,
) {
if system_config.project_name.is_empty() {
issues.push("System: project_name cannot be empty");
}
if system_config.max_concurrent_operations == 0 {
issues.push("System: max_concurrent_operations must be > 0");
}
}
fn validate_quality_config(
quality_config: &crate::services::configuration_service::QualityConfig,
issues: &mut Vec<&'static str>,
) {
if quality_config.max_complexity == 0 {
issues.push("Quality: max_complexity must be > 0");
}
if quality_config.min_coverage > 100.0 || quality_config.min_coverage < 0.0 {
issues.push("Quality: min_coverage must be between 0 and 100");
}
}
fn validate_analysis_config(
analysis_config: &crate::services::configuration_service::AnalysisConfig,
issues: &mut Vec<&'static str>,
) {
if analysis_config.max_file_size == 0 {
issues.push("Analysis: max_file_size must be > 0");
}
if analysis_config.timeout_seconds == 0 {
issues.push("Analysis: timeout_seconds must be > 0");
}
}
fn validate_performance_config(
performance_config: &crate::services::configuration_service::PerformanceConfig,
issues: &mut Vec<&'static str>,
) {
if performance_config.test_iterations == 0 {
issues.push("Performance: test_iterations must be > 0");
}
}
fn validate_mcp_config(
mcp_config: &crate::services::configuration_service::McpConfig,
issues: &mut Vec<&'static str>,
) {
if mcp_config.server_name.is_empty() {
issues.push("MCP: server_name cannot be empty");
}
if mcp_config.request_timeout_seconds == 0 {
issues.push("MCP: request_timeout_seconds must be > 0");
}
}
fn report_validation_results(config: &PmatConfig, issues: &[&str]) -> Result<()> {
if issues.is_empty() {
report_validation_success();
} else {
report_validation_failure(issues)?;
}
print_configuration_statistics(config);
Ok(())
}
fn report_validation_success() {
println!("✅ Configuration is valid");
println!(" All settings are within acceptable ranges");
println!(" No issues detected");
}
fn report_validation_failure(issues: &[&str]) -> Result<()> {
println!("❌ Configuration validation failed");
println!(" Found {} issues:", issues.len());
for issue in issues {
println!(" - {issue}");
}
Err(anyhow::anyhow!("Configuration validation failed"))
}
fn print_configuration_statistics(config: &PmatConfig) {
println!();
println!("📊 Configuration Statistics:");
println!(" Sections: 7");
println!(" Total Settings: ~50");
println!(" Custom Settings: {}", config.custom.len());
}
async fn reset_configuration(config_service: &ConfigurationService) -> Result<()> {
info!("🔄 Resetting configuration to defaults");
config_service
.update_config(|config| {
*config =
crate::services::configuration_service::ConfigurationService::default_config();
Ok(())
})
.await?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::tempdir;
#[tokio::test]
async fn test_configuration_overview() {
let temp_dir = tempdir().unwrap();
let config_path = temp_dir.path().join("test_config.toml");
let config_service = std::sync::Arc::new(ConfigurationService::new(Some(config_path)));
let result = show_configuration_overview(&config_service).await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_configuration_validation() {
let temp_dir = tempdir().unwrap();
let config_path = temp_dir.path().join("test_config.toml");
let config_service = std::sync::Arc::new(ConfigurationService::new(Some(config_path)));
let result = validate_configuration(&config_service).await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_set_configuration_values() {
let temp_dir = tempdir().unwrap();
let config_path = temp_dir.path().join("test_config.toml");
let config_service = std::sync::Arc::new(ConfigurationService::new(Some(config_path)));
let set_values = vec![
"quality.max_complexity=25".to_string(),
"system.verbose=true".to_string(),
];
let result = set_configuration_values(&config_service, set_values).await;
assert!(result.is_ok());
let config = config_service.get_config().unwrap();
assert_eq!(config.quality.max_complexity, 25);
assert!(config.system.verbose);
}
#[tokio::test]
async fn test_show_configuration_section() {
let config = crate::services::configuration_service::ConfigurationService::default_config();
let result = show_configuration_section(&config, "quality");
assert!(result.is_ok());
let result = show_configuration_section(&config, "invalid");
assert!(result.is_err());
}
#[tokio::test]
async fn test_reset_configuration() {
let temp_dir = tempdir().unwrap();
let config_path = temp_dir.path().join("test_config.toml");
let config_service = std::sync::Arc::new(ConfigurationService::new(Some(config_path)));
config_service
.update_config(|config| {
config.quality.max_complexity = 50;
Ok(())
})
.await
.unwrap();
let result = reset_configuration(&config_service).await;
assert!(result.is_ok());
let config = config_service.get_config().unwrap();
assert_eq!(config.quality.max_complexity, 20); }
}
#[cfg(test)]
mod property_tests {
use proptest::prelude::*;
proptest! {
#[test]
fn basic_property_stability(_input in ".*") {
prop_assert!(true);
}
#[test]
fn module_consistency_check(_x in 0u32..1000) {
prop_assert!(_x < 1001);
}
}
}