use std::fs;
use std::io::{self, BufRead, Write};
use std::process;
use crate::cli::show_projects_table;
use crate::config::{ConfigManager, Project};
use crate::git;
pub fn run_list(config: &ConfigManager) {
let projects = match config.projects_config.as_ref() {
Some(pc) => &pc.projects,
None => {
eprintln!("Projects config not loaded");
return;
}
};
if projects.is_empty() {
println!("No projects configured.");
return;
}
let rows: Vec<(String, String, String, String)> = projects
.iter()
.map(|p| (p.name.clone(), p.git_server_alias.clone(), p.git_path.clone(), p.path.clone()))
.collect();
show_projects_table(&rows);
}
pub fn run_add(config: &mut ConfigManager) {
let stdin = io::stdin();
let stdout = io::stdout();
let name = prompt(&stdin, &stdout, "Project name: ");
let server_alias = prompt(&stdin, &stdout, "Server alias: ");
let git_path = prompt(&stdin, &stdout, "Git path: ");
let local_path = prompt(&stdin, &stdout, "Local path: ");
if let Err(e) = config.get_server_url(&server_alias) {
eprintln!("Invalid server alias '{}': {}", server_alias, e);
return;
}
let project = Project {
name: name.clone(),
git_server_alias: server_alias,
git_path,
path: local_path.clone(),
};
let commit_msg = format!("repo: add project {}", name);
if register_project(config, project, &local_path, &commit_msg) {
println!("Project added.");
}
}
pub fn run_create(config: &mut ConfigManager) {
let stdin = io::stdin();
let stdout = io::stdout();
let name = prompt(&stdin, &stdout, "Project name: ");
let server_alias = prompt(&stdin, &stdout, "Server alias: ");
let git_path = prompt(&stdin, &stdout, "Git path: ");
let local_path = prompt(&stdin, &stdout, "Local path: ");
if let Err(e) = config.get_server_url(&server_alias) {
eprintln!("Invalid server alias '{}': {}", server_alias, e);
return;
}
let project = Project {
name: name.clone(),
git_server_alias: server_alias,
git_path,
path: local_path.clone(),
};
let dir = config.project_dir(&project);
if dir.exists() {
eprintln!("Directory already exists: {}", dir.display());
process::exit(1);
}
if let Err(e) = fs::create_dir_all(&dir) {
eprintln!("Failed to create directory '{}': {}", dir.display(), e);
process::exit(1);
}
if let Err(e) = git::run_git(&dir, &["init"]) {
eprintln!("Failed to run git init in '{}': {}", dir.display(), e);
let _ = fs::remove_dir_all(&dir);
process::exit(1);
}
let commit_msg = format!("repo: create project {}", name);
if register_project(config, project, &local_path, &commit_msg) {
println!("Project created.");
}
}
fn register_project(
config: &mut ConfigManager,
project: Project,
local_path: &str,
commit_msg: &str,
) -> bool {
let pc = match config.projects_config.as_mut() {
Some(pc) => pc,
None => {
eprintln!("Projects config not loaded");
return false;
}
};
pc.projects.push(project);
if let Err(e) = config.save_projects_config() {
eprintln!("Failed to save projects config: {}", e);
return false;
}
let (autoignore, autocommit) = config
.local_config
.as_ref()
.map(|lc| (lc.autoignore, lc.autocommit))
.unwrap_or((false, false));
if autoignore {
let gitignore_path = config.project_root.join(".gitignore");
let existing = fs::read_to_string(&gitignore_path).unwrap_or_default();
let already_present = existing.lines().any(|l| l == local_path);
if !already_present {
let mut content = existing;
if !content.is_empty() && !content.ends_with('\n') {
content.push('\n');
}
content.push_str(local_path);
content.push('\n');
if let Err(e) = fs::write(&gitignore_path, &content) {
eprintln!("Warning: failed to update .gitignore: {}", e);
}
}
}
if autocommit {
let mut files = vec!["projects.json"];
if autoignore {
files.push(".gitignore");
}
let mut add_args = vec!["add"];
add_args.extend_from_slice(&files);
if let Err(e) = git::run_git(&config.project_root, &add_args) {
eprintln!("Warning: git add failed: {}", e);
} else if let Err(e) = git::run_git(&config.project_root, &["commit", "-m", commit_msg]) {
eprintln!("Warning: git commit failed: {}", e);
}
}
true
}
pub fn run_remove(config: &mut ConfigManager, path: Option<String>) {
let stdin = io::stdin();
let stdout = io::stdout();
let target_path = match path {
Some(p) => p,
None => prompt(&stdin, &stdout, "Local path of project to remove: "),
};
let pc = match config.projects_config.as_mut() {
Some(pc) => pc,
None => {
eprintln!("Projects config not loaded");
return;
}
};
let original_len = pc.projects.len();
pc.projects.retain(|p| p.path != target_path);
if pc.projects.len() == original_len {
eprintln!("No project found with local path '{}'", target_path);
return;
}
if let Err(e) = config.save_projects_config() {
eprintln!("Failed to save projects config: {}", e);
return;
}
let autocommit = config
.local_config
.as_ref()
.map(|lc| lc.autocommit)
.unwrap_or(false);
if autocommit {
if let Err(e) = git::run_git(&config.project_root, &["add", "projects.json"]) {
eprintln!("Warning: git add failed: {}", e);
} else {
let msg = format!("repo: remove project {}", target_path);
if let Err(e) = git::run_git(&config.project_root, &["commit", "-m", &msg]) {
eprintln!("Warning: git commit failed: {}", e);
}
}
}
println!("Project with path '{}' removed.", target_path);
}
fn prompt(stdin: &io::Stdin, stdout: &io::Stdout, message: &str) -> String {
let mut out = stdout.lock();
write!(out, "{}", message).unwrap();
out.flush().unwrap();
drop(out);
let mut line = String::new();
stdin.lock().read_line(&mut line).unwrap_or(0);
line.trim().to_string()
}