use crate::config::Dependency;
use anyhow::{Context, Result};
use colored::*;
use std::collections::HashMap;
use std::fs;
use std::path::Path;
use std::process::Command;
pub fn add_dependency(
lib_input: &str,
tag: Option<String>,
branch: Option<String>,
rev: Option<String>,
) -> Result<()> {
if !Path::new("cx.toml").exists() {
println!("{} Error: cx.toml not found.", "x".red());
return Ok(());
}
let (name, url) = if let Some(resolved_url) = crate::registry::resolve_alias(lib_input) {
(lib_input.to_string(), resolved_url)
} else if lib_input.contains("http") || lib_input.contains("git@") {
let name = lib_input
.split('/')
.next_back()
.unwrap_or("unknown")
.replace(".git", "");
(name, lib_input.to_string())
} else {
let parts: Vec<&str> = lib_input.split('/').collect();
if parts.len() != 2 {
println!(
"{} Invalid format. Use 'alias', 'user/repo', or full URL.",
"x".red()
);
return Ok(());
}
let name = parts[1].to_string();
let url = format!("https://github.com/{}.git", lib_input);
(name, url)
};
println!("{} Adding dependency: {}...", "📦".blue(), name.bold());
let config_str = fs::read_to_string("cx.toml")?;
let mut config: crate::config::CxConfig = toml::from_str(&config_str)?;
if config.dependencies.is_none() {
config.dependencies = Some(HashMap::new());
}
let dep_entry = if tag.is_none() && branch.is_none() && rev.is_none() {
Dependency::Simple(url.clone())
} else {
Dependency::Complex {
git: Some(url.clone()),
pkg: None,
branch,
tag,
rev,
build: None,
output: None,
}
};
if let Some(deps) = &mut config.dependencies {
if deps.contains_key(&name) {
println!("! Dependency '{}' updated.", name);
}
deps.insert(name.clone(), dep_entry);
}
let new_toml = toml::to_string_pretty(&config)?;
fs::write("cx.toml", new_toml)?;
println!("{} Added {} to cx.toml", "✓".green(), name);
if let Some(deps) = &config.dependencies {
let _ = super::fetch::fetch_dependencies(deps)?;
}
Ok(())
}
pub fn remove_dependency(name: &str) -> Result<()> {
if !Path::new("cx.toml").exists() {
println!("{} Error: cx.toml not found.", "x".red());
return Ok(());
}
let config_str = fs::read_to_string("cx.toml")?;
let mut config: crate::config::CxConfig = toml::from_str(&config_str)?;
let mut found = false;
if let Some(deps) = &mut config.dependencies
&& deps.remove(name).is_some()
{
found = true;
}
if found {
let new_toml = toml::to_string_pretty(&config)?;
fs::write("cx.toml", new_toml)?;
println!("{} Removed dependency: {}", "🗑️".red(), name.bold());
} else {
println!(
"{} Dependency '{}' not found in cx.toml",
"!".yellow(),
name
);
}
Ok(())
}
pub fn update_dependencies() -> Result<()> {
if !Path::new("cx.toml").exists() {
println!("{} Error: cx.toml not found.", "x".red());
return Ok(());
}
println!("{} Checking for updates...", "📦".blue());
let config_str = fs::read_to_string("cx.toml")?;
let config: crate::config::CxConfig = toml::from_str(&config_str)?;
let home_dir = dirs::home_dir().context("Could not find home directory")?;
let cache_dir = home_dir.join(".cx").join("cache");
if let Some(deps) = config.dependencies {
for (name, dep_data) in deps {
let is_git = matches!(
dep_data,
crate::config::Dependency::Simple(_)
| crate::config::Dependency::Complex { git: Some(_), .. }
);
if is_git {
let lib_path = cache_dir.join(&name);
if lib_path.exists() {
print!(" Updating {} ... ", name);
if let Ok(repo) = git2::Repository::open(&lib_path) {
let mut remote = repo.find_remote("origin")?;
remote.fetch(&["HEAD"], None, None)?;
let command = "git fetch origin && git reset --hard origin/HEAD";
let status = if cfg!(target_os = "windows") {
Command::new("cmd")
.args(["/C", command])
.current_dir(&lib_path)
.output()
} else {
Command::new("sh")
.args(["-c", command])
.current_dir(&lib_path)
.output()
};
if let Ok(out) = status {
if out.status.success() {
println!("{}", "✓".green());
} else {
let err = String::from_utf8_lossy(&out.stderr);
println!("{} (git update failed: {})", "x".red(), err.trim());
}
} else {
println!("{}", "Error executing git".red());
}
} else {
println!("{}", "Not a valid git repo".yellow());
}
}
}
}
}
println!("{} Dependencies updated.", "✓".green());
Ok(())
}