use crate::error_recovery;
use crate::file_analyzer::FileAnalyzer;
use crate::module_generator::{extract_test_module_path, generate_mod_rs_ext};
use crate::workspace;
use crate::Args;
use anyhow::Result;
use std::collections::{HashMap, HashSet};
use std::fs;
use std::path::{Path, PathBuf};
use syn::Item;
pub(crate) fn run_workspace_mode(args: &Args) -> Result<()> {
use rayon::prelude::*;
use workspace::{ParallelProcessor, WorkspaceAnalyzer};
println!("📦 SplitRS Workspace Mode");
println!("{}", "=".repeat(60));
if args.parallel {
let processor = ParallelProcessor::new(args.threads);
processor.configure_pool()?;
if args.threads > 0 {
println!(" Parallel processing: {} threads", args.threads);
} else {
println!(" Parallel processing: auto (all available cores)");
}
}
let ws_input = args
.input
.as_deref()
.unwrap_or_else(|| std::path::Path::new("."));
let analyzer = WorkspaceAnalyzer::new(ws_input, args.target);
let analysis = analyzer.analyze()?;
analyzer.print_summary(&analysis);
if args.dry_run {
println!("\n{}", "=".repeat(60));
println!("DRY RUN - No changes made");
println!("{}", "=".repeat(60));
return Ok(());
}
if analysis.files_to_refactor.is_empty() {
println!("\n✅ No files need refactoring");
return Ok(());
}
println!(
"\n🔧 Processing {} files...",
analysis.files_to_refactor.len()
);
let rollback_manager = error_recovery::RollbackManager::new(args.rollback);
let mut error_collector =
error_recovery::ErrorCollector::new().with_continue_on_error(args.continue_on_error);
let mut processed = 0;
let mut failed = 0;
let ws_output = args
.output
.clone()
.unwrap_or_else(|| std::path::PathBuf::from("."));
let results: Vec<_> = if args.parallel {
analysis
.files_to_refactor
.par_iter()
.map(|file_info| {
process_workspace_file(
&file_info.path,
&ws_output,
args.max_lines.unwrap_or(args.target),
args.continue_on_error,
)
})
.collect()
} else {
analysis
.files_to_refactor
.iter()
.map(|file_info| {
process_workspace_file(
&file_info.path,
&ws_output,
args.max_lines.unwrap_or(args.target),
args.continue_on_error,
)
})
.collect()
};
for result in results {
match result {
Ok(path) => {
println!(" ✅ Processed: {:?}", path);
processed += 1;
}
Err(e) => {
let error = error_recovery::DiagnosticError::new(
e.to_string(),
error_recovery::ErrorSeverity::Error,
);
let should_continue = error_collector.add(error);
failed += 1;
if !should_continue {
eprintln!(" ❌ Too many errors, stopping...");
if args.rollback {
eprintln!(" 🔄 Rolling back changes...");
rollback_manager.rollback()?;
}
break;
}
}
}
}
println!("\n📊 Workspace Refactoring Summary");
println!("{}", "=".repeat(60));
println!(" Files processed: {}", processed);
println!(" Files failed: {}", failed);
if error_collector.has_errors() {
println!("\n⚠️ Errors encountered:");
print!("{}", error_collector.format_all());
}
if args.rollback && failed > 0 {
println!("\n🔄 Some files failed. Use --rollback to restore original files.");
}
Ok(())
}
pub(crate) fn process_workspace_file(
input: &Path,
output_base: &Path,
max_lines: usize,
_continue_on_error: bool,
) -> Result<PathBuf> {
let file_stem = input
.file_stem()
.ok_or_else(|| anyhow::anyhow!("Invalid file name"))?;
let output = output_base.join(file_stem);
fs::create_dir_all(&output)?;
let source_code = fs::read_to_string(input)?;
let syntax_tree = syn::parse_file(&source_code)?;
let mut analyzer = FileAnalyzer::new(true, max_lines / 2);
analyzer.set_source(&source_code);
analyzer.analyze_with_test_files(&syntax_tree, input);
let file_backed_mods = analyzer.take_file_backed_mods();
let pinned_root_mod_names: HashSet<String> = file_backed_mods
.iter()
.map(|m| m.ident.to_string())
.collect();
let modules = analyzer.group_by_module(max_lines);
let mut type_to_module: HashMap<String, String> = HashMap::new();
for module in &modules {
for exported_type in module.importable_exported_names() {
type_to_module.insert(exported_type, module.name.clone());
}
}
for module in &modules {
for item in &module.standalone_items {
if let Item::Trait(trait_item) = item {
let trait_name = trait_item.ident.to_string();
analyzer
.trait_tracker
.register_trait_module(&trait_name, &module.name);
}
}
}
let (needs_pub_super, cross_module_imports, fields_need_pub_super) =
analyzer.compute_cross_module_visibility(&modules);
for module in &modules {
let module_path = output.join(format!("{}.rs", module.name));
let content = module.generate_content(
&syntax_tree,
&analyzer.use_statements,
&type_to_module,
&needs_pub_super,
cross_module_imports.get(&module.name),
&fields_need_pub_super,
Some(&analyzer.trait_tracker),
&pinned_root_mod_names,
);
fs::write(&module_path, &content)?;
}
let lib_rs_check = output.join("lib.rs");
if !lib_rs_check.exists() {
let test_module_path = extract_test_module_path(&syntax_tree);
let mod_rs_path = output.join("mod.rs");
let mod_content = generate_mod_rs_ext(
&modules,
&output,
test_module_path.as_deref(),
false,
&[],
&[],
&file_backed_mods,
crate::config::FacadeStyle::Glob,
&[],
)?;
fs::write(&mod_rs_path, &mod_content)?;
}
Ok(output)
}