use crate::cli::{ExplainLevel, RefactorCommands, RefactorMode, RefactorOutputFormat};
use crate::models::refactor::{RefactorConfig, Summary};
use crate::services::cache::unified_manager::UnifiedCacheManager;
use crate::services::refactor_engine::{EngineMode, UnifiedEngine};
use crate::services::unified_ast_engine::UnifiedAstEngine;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::time::Duration;
pub struct RefactorServeParams {
pub mode: RefactorMode,
pub config: Option<PathBuf>,
pub project: PathBuf,
pub parallel: usize,
pub memory_limit: usize,
pub batch_size: usize,
pub priority: Option<String>,
pub checkpoint_dir: Option<PathBuf>,
pub resume: bool,
pub auto_commit: Option<String>,
pub max_runtime: Option<u64>,
}
pub async fn route_refactor_command(refactor_cmd: RefactorCommands) -> anyhow::Result<()> {
match refactor_cmd {
RefactorCommands::Serve {
refactor_mode,
config,
project,
parallel,
memory_limit,
batch_size,
priority,
checkpoint_dir,
resume,
auto_commit,
max_runtime,
} => {
let params = RefactorServeParams {
mode: refactor_mode,
config,
project,
parallel,
memory_limit,
batch_size,
priority,
checkpoint_dir,
resume,
auto_commit,
max_runtime,
};
handle_refactor_serve(params).await
}
RefactorCommands::Interactive {
project_path,
explain,
checkpoint,
target_complexity,
steps,
config,
} => {
handle_refactor_interactive(
project_path,
explain,
checkpoint,
target_complexity,
steps,
config,
)
.await
}
RefactorCommands::Status { checkpoint, format } => {
handle_refactor_status(checkpoint, format).await
}
RefactorCommands::Resume {
checkpoint,
steps,
explain,
} => handle_refactor_resume(checkpoint, steps, explain).await,
RefactorCommands::Auto {
project_path,
single_file_mode,
file,
max_iterations,
quality_profile: _,
format,
dry_run,
skip_compilation: _,
skip_tests: _,
checkpoint,
verbose: _,
exclude,
include,
ignore_file,
test,
test_name,
github_issue,
bug_report_path,
} => {
super::refactor_auto_handlers::handle_refactor_auto(
super::refactor_auto_handlers::RefactorAutoConfig {
project_path,
single_file_mode,
file,
format,
max_iterations,
cache_dir: checkpoint,
dry_run,
ci_mode: false, exclude_patterns: exclude,
include_patterns: include,
ignore_file,
test_file: test,
test_name,
github_issue_url: github_issue,
bug_report_path,
},
)
.await
}
RefactorCommands::Docs {
project_path,
include_docs,
include_root,
additional_dirs,
format,
dry_run,
temp_patterns,
status_patterns,
artifact_patterns,
custom_patterns,
min_age_days,
max_size_mb,
recursive,
preserve_patterns,
output,
auto_remove,
backup,
backup_dir,
perf,
} => {
super::refactor_docs_handlers::handle_refactor_docs(
project_path,
include_docs,
include_root,
additional_dirs,
format,
dry_run,
temp_patterns,
status_patterns,
artifact_patterns,
custom_patterns,
min_age_days,
max_size_mb,
recursive,
preserve_patterns,
output,
auto_remove,
backup,
backup_dir,
perf,
)
.await
}
}
}
pub async fn handle_refactor_serve(params: RefactorServeParams) -> anyhow::Result<()> {
let extracted_params = extract_refactor_params(params);
log_refactor_server_startup(&extracted_params);
let refactor_config = setup_refactor_configuration(&extracted_params).await?;
let checkpoint_path = setup_checkpoint_directory(&extracted_params).await?;
let (cache, ast_engine) = setup_cache_and_ast_engine(extracted_params.memory_limit)?;
let engine_mode = create_engine_mode(&extracted_params, &checkpoint_path);
let targets = discover_and_prioritize_targets(&extracted_params, &refactor_config).await?;
let summary = execute_refactor_engine(
ast_engine,
cache,
engine_mode,
refactor_config.clone(),
targets,
extracted_params.max_runtime,
)
.await?;
print_refactor_summary(&summary);
handle_auto_commit(&refactor_config, &summary).await?;
Ok(())
}
struct ExtractedRefactorParams {
mode: RefactorMode,
config: Option<PathBuf>,
project: PathBuf,
parallel: usize,
memory_limit: usize,
batch_size: usize,
priority: Option<String>,
checkpoint_dir: Option<PathBuf>,
resume: bool,
auto_commit: Option<String>,
max_runtime: Option<u64>,
}
fn extract_refactor_params(params: RefactorServeParams) -> ExtractedRefactorParams {
let RefactorServeParams {
mode,
config,
project,
parallel,
memory_limit,
batch_size,
priority,
checkpoint_dir,
resume,
auto_commit,
max_runtime,
} = params;
ExtractedRefactorParams {
mode,
config,
project,
parallel,
memory_limit,
batch_size,
priority,
checkpoint_dir,
resume,
auto_commit,
max_runtime,
}
}
fn log_refactor_server_startup(params: &ExtractedRefactorParams) {
println!("🔧 Starting refactor server mode...");
println!("📁 Project: {}", params.project.display());
println!("⚙️ Mode: {:?}", params.mode);
println!("🔄 Parallel workers: {}", params.parallel);
println!("💾 Memory limit: {}MB", params.memory_limit);
println!("📦 Batch size: {} files", params.batch_size);
}
async fn setup_refactor_configuration(
params: &ExtractedRefactorParams,
) -> anyhow::Result<RefactorConfig> {
let mut refactor_config = load_base_configuration(params).await?;
apply_command_line_overrides(&mut refactor_config, params);
Ok(refactor_config)
}
async fn load_base_configuration(
params: &ExtractedRefactorParams,
) -> anyhow::Result<RefactorConfig> {
if let Some(config_path) = ¶ms.config {
println!("📋 Loading config from: {}", config_path.display());
load_refactor_config_json(config_path).await
} else {
Ok(RefactorConfig::default())
}
}
fn apply_command_line_overrides(config: &mut RefactorConfig, params: &ExtractedRefactorParams) {
if let Some(prio) = ¶ms.priority {
println!("🎯 Priority expression: {prio}");
config.priority_expression = Some(prio.clone());
}
if let Some(commit_template) = ¶ms.auto_commit {
println!("🔗 Auto-commit template: {commit_template}");
config.auto_commit_template = Some(commit_template.clone());
}
config.parallel_workers = params.parallel;
config.memory_limit_mb = params.memory_limit;
config.batch_size = params.batch_size;
}
async fn setup_checkpoint_directory(params: &ExtractedRefactorParams) -> anyhow::Result<PathBuf> {
let checkpoint_path = params
.checkpoint_dir
.clone()
.unwrap_or_else(|| params.project.join(".refactor_checkpoints"));
if params.resume {
println!(
"🔄 Resuming from checkpoint in: {}",
checkpoint_path.display()
);
} else {
tokio::fs::create_dir_all(&checkpoint_path).await?;
}
Ok(checkpoint_path)
}
fn setup_cache_and_ast_engine(
memory_limit: usize,
) -> anyhow::Result<(Arc<UnifiedCacheManager>, Arc<UnifiedAstEngine>)> {
let cache_config = crate::services::cache::unified::UnifiedCacheConfig {
max_memory_mb: memory_limit / 2, ..Default::default()
};
let cache = Arc::new(UnifiedCacheManager::new(cache_config)?);
let ast_engine = Arc::new(UnifiedAstEngine::new());
Ok((cache, ast_engine))
}
fn create_engine_mode(params: &ExtractedRefactorParams, checkpoint_path: &Path) -> EngineMode {
match params.mode {
RefactorMode::Batch => EngineMode::Batch {
checkpoint_dir: checkpoint_path.to_path_buf(),
resume: params.resume,
parallel_workers: params.parallel,
},
RefactorMode::Interactive => EngineMode::Interactive {
checkpoint_file: checkpoint_path.join("interactive_state.json"),
explain_level: crate::services::refactor_engine::ExplainLevel::Detailed,
},
}
}
async fn discover_and_prioritize_targets(
params: &ExtractedRefactorParams,
config: &RefactorConfig,
) -> anyhow::Result<Vec<PathBuf>> {
let mut targets = discover_refactor_targets(¶ms.project).await?;
if let Some(priority_expr) = &config.priority_expression {
println!(
"🔀 Sorting {} targets by priority expression",
targets.len()
);
targets = sort_targets_by_priority(targets, priority_expr).await?;
}
println!("🎯 Found {} refactoring targets", targets.len());
Ok(targets)
}
async fn execute_refactor_engine(
ast_engine: Arc<UnifiedAstEngine>,
cache: Arc<UnifiedCacheManager>,
engine_mode: EngineMode,
refactor_config: RefactorConfig,
targets: Vec<PathBuf>,
max_runtime: Option<u64>,
) -> anyhow::Result<Summary> {
let mut engine = UnifiedEngine::new(ast_engine, cache, engine_mode, refactor_config, targets);
if let Some(runtime_seconds) = max_runtime {
execute_with_timeout(engine, runtime_seconds).await
} else {
engine.run().await.map_err(anyhow::Error::from)
}
}
async fn execute_with_timeout(
mut engine: UnifiedEngine,
runtime_seconds: u64,
) -> anyhow::Result<Summary> {
let limit = Duration::from_secs(runtime_seconds);
println!("⏱️ Maximum runtime: {} seconds", limit.as_secs());
let result = tokio::time::timeout(limit, engine.run()).await;
if let Ok(summary) = result { summary.map_err(anyhow::Error::from) } else {
println!("⏰ Runtime limit reached, saving checkpoint...");
engine.save_checkpoint().await?;
std::process::exit(0);
}
}
fn print_refactor_summary(summary: &Summary) {
let start_time = std::time::Instant::now();
println!("\n✅ Refactor server completed:");
println!(" Files processed: {}", summary.files_processed);
println!(" Refactors applied: {}", summary.refactors_applied);
println!(
" Complexity reduction: {:.2}%",
summary.complexity_reduction
);
println!(" SATD removed: {}", summary.satd_removed);
println!(" Runtime: {:.2}s", start_time.elapsed().as_secs_f64());
}
async fn handle_auto_commit(config: &RefactorConfig, summary: &Summary) -> anyhow::Result<()> {
if let Some(commit_template) = &config.auto_commit_template {
if summary.refactors_applied > 0 {
println!("\n📝 Creating auto-commit...");
create_auto_commit(commit_template, summary).await?;
}
}
Ok(())
}
pub async fn handle_refactor_interactive(
project_path: PathBuf,
explain: ExplainLevel,
checkpoint: PathBuf,
target_complexity: u16,
steps: Option<u32>,
config: Option<PathBuf>,
) -> anyhow::Result<()> {
println!("🤖 Starting interactive refactor mode...");
println!("📁 Project path: {}", project_path.display());
println!("💾 Checkpoint: {}", checkpoint.display());
println!("🎯 Target complexity: {target_complexity}");
println!("📝 Explanation level: {explain:?}");
let refactor_config = if let Some(config_path) = config {
load_refactor_config(&config_path).await?
} else {
RefactorConfig {
target_complexity,
..Default::default()
}
};
let cache_config = crate::services::cache::unified::UnifiedCacheConfig::default();
let cache = Arc::new(UnifiedCacheManager::new(cache_config)?);
let ast_engine = Arc::new(UnifiedAstEngine::new());
let mode = EngineMode::Interactive {
checkpoint_file: checkpoint,
explain_level: explain.into(),
};
let targets = discover_refactor_targets(&project_path).await?;
println!("🎯 Found {} refactoring targets", targets.len());
let mut engine = UnifiedEngine::new(ast_engine, cache, mode, refactor_config, targets);
if let Some(max_steps) = steps {
println!("⏱️ Maximum steps: {max_steps}");
}
let summary = engine.run().await?;
println!("✅ Interactive refactor completed:");
println!(" Files processed: {}", summary.files_processed);
println!(" Refactors applied: {}", summary.refactors_applied);
Ok(())
}
pub async fn handle_refactor_status(
checkpoint: PathBuf,
format: RefactorOutputFormat,
) -> anyhow::Result<()> {
println!("📊 Reading refactor status from: {}", checkpoint.display());
validate_checkpoint_file(checkpoint.as_path())?;
let checkpoint_data = read_checkpoint_data(&checkpoint).await?;
format_refactor_status(&checkpoint_data, format, &checkpoint)?;
Ok(())
}
fn validate_checkpoint_file(checkpoint: &Path) -> anyhow::Result<()> {
if !checkpoint.exists() {
return Err(anyhow::anyhow!(
"Checkpoint file not found: {}",
checkpoint.display()
));
}
Ok(())
}
async fn read_checkpoint_data(checkpoint: &PathBuf) -> anyhow::Result<String> {
tokio::fs::read_to_string(checkpoint)
.await
.map_err(Into::into)
}
fn format_refactor_status(
checkpoint_data: &str,
format: RefactorOutputFormat,
checkpoint: &PathBuf,
) -> anyhow::Result<()> {
match format {
RefactorOutputFormat::Json => format_as_json(checkpoint_data),
RefactorOutputFormat::Table => format_as_table(checkpoint_data),
RefactorOutputFormat::Summary => format_as_summary(checkpoint_data, checkpoint.as_path()),
}
}
fn format_as_json(checkpoint_data: &str) -> anyhow::Result<()> {
let parsed: serde_json::Value = serde_json::from_str(checkpoint_data)?;
println!("{}", serde_json::to_string_pretty(&parsed)?);
Ok(())
}
fn format_as_table(checkpoint_data: &str) -> anyhow::Result<()> {
let state: serde_json::Value = serde_json::from_str(checkpoint_data)?;
print_table_header();
print_table_data(&state);
print_table_footer();
Ok(())
}
fn print_table_header() {
println!("┌─────────────────┬──────────────────────────────────────┐");
println!("│ Property │ Value │");
println!("├─────────────────┼──────────────────────────────────────┤");
}
fn print_table_data(state: &serde_json::Value) {
if let Some(current) = state.get("current") {
println!(
"│ Current State │ {:36} │",
format!("{current:?}")
.chars()
.take(36)
.collect::<String>()
);
}
if let Some(targets) = state.get("targets") {
if let Some(targets_array) = targets.as_array() {
println!("│ Target Count │ {:36} │", targets_array.len());
}
}
if let Some(index) = state.get("current_target_index") {
println!("│ Current Index │ {:36} │", index.as_u64().unwrap_or(0));
}
}
fn print_table_footer() {
println!("└─────────────────┴──────────────────────────────────────┘");
}
fn format_as_summary(checkpoint_data: &str, checkpoint: &Path) -> anyhow::Result<()> {
let state: serde_json::Value = serde_json::from_str(checkpoint_data)?;
println!("🔧 Refactor Status Summary");
println!(" Checkpoint: {}", checkpoint.display());
if let Some(current) = state.get("current") {
println!(" Current state: {current:?}");
}
if let Some(targets) = state.get("targets") {
if let Some(targets_array) = targets.as_array() {
println!(" Total targets: {}", targets_array.len());
}
}
Ok(())
}
pub async fn handle_refactor_resume(
checkpoint: PathBuf,
steps: u32,
explain: Option<ExplainLevel>,
) -> anyhow::Result<()> {
println!("🔄 Resuming refactor from: {}", checkpoint.display());
println!("⏱️ Maximum steps: {steps}");
if !checkpoint.exists() {
return Err(anyhow::anyhow!(
"Checkpoint file not found: {}",
checkpoint.display()
));
}
let checkpoint_data = tokio::fs::read_to_string(&checkpoint).await?;
let _state: serde_json::Value = serde_json::from_str(&checkpoint_data)?;
println!("📝 State loaded successfully");
if let Some(explain_level) = explain {
println!("📖 Explanation level override: {explain_level:?}");
}
println!("⚠️ Resume functionality not yet fully implemented");
println!(
" This would continue from the saved state for {steps} steps"
);
Ok(())
}
async fn load_refactor_config(config_path: &Path) -> anyhow::Result<RefactorConfig> {
println!("📝 Loading config from: {}", config_path.display());
Ok(RefactorConfig::default())
}
async fn load_refactor_config_json(config_path: &Path) -> anyhow::Result<RefactorConfig> {
println!("📝 Loading JSON config from: {}", config_path.display());
let config_data = tokio::fs::read_to_string(config_path).await?;
let config: serde_json::Value = serde_json::from_str(&config_data)?;
let mut refactor_config = RefactorConfig::default();
if let Some(rules) = config.get("rules") {
if let Some(target_complexity) = rules.get("target_complexity").and_then(serde_json::Value::as_u64) {
refactor_config.target_complexity = target_complexity as u16;
}
if let Some(max_function_lines) = rules.get("max_function_lines").and_then(serde_json::Value::as_u64) {
refactor_config.max_function_lines = max_function_lines as u32;
}
if let Some(remove_satd) = rules.get("remove_satd").and_then(serde_json::Value::as_bool) {
refactor_config.remove_satd = remove_satd;
}
}
if let Some(parallel) = config.get("parallel_workers").and_then(serde_json::Value::as_u64) {
refactor_config.parallel_workers = parallel as usize;
}
if let Some(memory) = config.get("memory_limit_mb").and_then(serde_json::Value::as_u64) {
refactor_config.memory_limit_mb = memory as usize;
}
if let Some(batch) = config.get("batch_size").and_then(serde_json::Value::as_u64) {
refactor_config.batch_size = batch as usize;
}
if let Some(priority) = config.get("priority_expression").and_then(|v| v.as_str()) {
refactor_config.priority_expression = Some(priority.to_string());
}
if let Some(auto_commit) = config.get("auto_commit_template").and_then(|v| v.as_str()) {
refactor_config.auto_commit_template = Some(auto_commit.to_string());
}
Ok(refactor_config)
}
async fn sort_targets_by_priority(
mut targets: Vec<PathBuf>,
_priority_expr: &str,
) -> anyhow::Result<Vec<PathBuf>> {
targets.reverse();
Ok(targets)
}
async fn create_auto_commit(
template: &str,
summary: &crate::models::refactor::Summary,
) -> anyhow::Result<()> {
use std::process::Command;
let status = Command::new("git").args(["add", "-A"]).status()?;
if !status.success() {
return Err(anyhow::anyhow!("Failed to stage changes"));
}
let message = template
.replace("{files}", &summary.files_processed.to_string())
.replace("{refactors}", &summary.refactors_applied.to_string())
.replace(
"{complexity_reduction}",
&format!("{:.1}%", summary.complexity_reduction),
)
.replace("{satd_removed}", &summary.satd_removed.to_string());
let status = Command::new("git")
.args(["commit", "-m", &message])
.status()?;
if status.success() {
println!("✅ Auto-commit created: {message}");
} else {
println!("⚠️ Auto-commit failed");
}
Ok(())
}
async fn discover_refactor_targets(project_path: &PathBuf) -> anyhow::Result<Vec<PathBuf>> {
let mut targets = Vec::new();
let extensions = ["rs", "ts", "tsx", "js", "jsx", "py"];
for entry in walkdir::WalkDir::new(project_path) {
let entry = entry?;
if entry.file_type().is_file() {
if let Some(ext) = entry.path().extension() {
if extensions.contains(&ext.to_string_lossy().as_ref()) {
targets.push(entry.path().to_path_buf());
}
}
}
}
Ok(targets)
}
impl From<ExplainLevel> for crate::services::refactor_engine::ExplainLevel {
fn from(level: ExplainLevel) -> Self {
match level {
ExplainLevel::Brief => crate::services::refactor_engine::ExplainLevel::Brief,
ExplainLevel::Detailed => crate::services::refactor_engine::ExplainLevel::Detailed,
ExplainLevel::Verbose => crate::services::refactor_engine::ExplainLevel::Verbose,
}
}
}
#[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);
}
}
}