use anyhow::{Context, Result};
use std::fs;
use std::path::Path;
use crate::{BuildSystem, PackageManager};
pub fn detect_build_system() -> Result<Option<BuildSystem>> {
if Path::new("CMakeLists.txt").exists() {
Ok(Some(BuildSystem::CMake))
} else if Path::new("Makefile").exists() {
Ok(Some(BuildSystem::Makefile))
} else {
Ok(None)
}
}
pub fn detect_package_manager() -> Result<Option<PackageManager>> {
if Path::new("conanfile.txt").exists() || Path::new("conanfile.py").exists() {
Ok(Some(PackageManager::Conan))
} else if Path::new("vcpkg.json").exists() {
Ok(Some(PackageManager::Vcpkg))
} else {
Ok(None)
}
}
pub fn convert_build_system(from: BuildSystem, to: BuildSystem, project_name: &str) -> Result<()> {
convert_build_system_with_prompt(from, to, project_name, false)
}
pub fn convert_build_system_interactive(
from: BuildSystem,
to: BuildSystem,
project_name: &str,
) -> Result<()> {
convert_build_system_with_prompt(from, to, project_name, true)
}
fn convert_build_system_with_prompt(
from: BuildSystem,
to: BuildSystem,
project_name: &str,
interactive: bool,
) -> Result<()> {
if from == to {
anyhow::bail!("Project already uses {}. No conversion needed.", to);
}
let language = crate::languages::Language::from_project_structure_with_prompt(interactive)?;
match from {
BuildSystem::Makefile => {
if Path::new("Makefile").exists() {
fs::remove_file("Makefile").context("Failed to remove old Makefile")?;
println!("✓ Removed Makefile");
}
}
BuildSystem::CMake => {
if Path::new("CMakeLists.txt").exists() {
fs::remove_file("CMakeLists.txt").context("Failed to remove old CMakeLists.txt")?;
println!("✓ Removed CMakeLists.txt");
}
}
}
let generator = crate::get_generator(to);
let build_file_content = generator.generate_build_file(language, project_name);
fs::write(generator.extension(), build_file_content)
.context("Failed to write new build system file")?;
println!("✓ Successfully converted project from {} to {}", from, to);
Ok(())
}
pub fn add_package_manager_to_project(pm: PackageManager, project_name: &str) -> Result<()> {
if let Ok(Some(existing)) = detect_package_manager() {
if existing == pm {
anyhow::bail!("Project already uses {}. No changes needed.", pm);
} else {
println!(
"⚠️ Warning: Project already has {} configured. Adding {} as well.",
existing, pm
);
}
}
let pm_generator = crate::get_package_manager_generator(pm);
let manifest = pm_generator.generate_manifest(project_name);
fs::write(pm_generator.extension(), manifest)
.with_context(|| format!("Failed to write {} manifest", pm_generator.name()))?;
println!("✓ Generated {} configuration", pm);
println!(
"📝 Next steps: {}",
pm_generator.generate_install_instructions()
);
Ok(())
}
pub fn remove_package_manager_from_project(pm: PackageManager) -> Result<()> {
let pm_generator = crate::get_package_manager_generator(pm);
if !Path::new(pm_generator.extension()).exists() {
anyhow::bail!(
"{} not found in project. Nothing to remove.",
pm_generator.name()
);
}
fs::remove_file(pm_generator.extension())
.with_context(|| format!("Failed to remove {} file", pm_generator.name()))?;
println!("✓ Removed {} configuration", pm);
Ok(())
}
pub fn list_features() -> Result<()> {
println!("\n📦 Project Features:");
println!("====================\n");
match detect_build_system()? {
Some(bs) => println!(" Build System: {}", bs),
None => println!(" Build System: (none detected)"),
}
match detect_package_manager()? {
Some(pm) => println!(" Package Manager: {}", pm),
None => println!(" Package Manager: (none configured)"),
}
let has_src = Path::new("src").exists();
let has_vscode = Path::new(".vscode").exists();
let has_gitignore = Path::new(".gitignore").exists();
let has_clang_format = Path::new(".clang-format").exists();
println!(" Src directory: {}", if has_src { "✓" } else { "✗" });
println!(" VSCode config: {}", if has_vscode { "✓" } else { "✗" });
println!(
" .gitignore: {}",
if has_gitignore { "✓" } else { "✗" }
);
println!(
" .clang-format: {}",
if has_clang_format { "✓" } else { "✗" }
);
println!("\n");
Ok(())
}