use std::{ fs, path::{Path, PathBuf}, }; use crate::{ cli::RemoteArgs, error::{PakkerError, Result}, fetch::Fetcher, git, model::{config::Config, lockfile::LockFile}, }; const REMOTE_DIR: &str = ".pakku-remote"; pub async fn execute(args: RemoteArgs) -> Result<()> { let remote_path = PathBuf::from(REMOTE_DIR); // Handle --remove flag if args.remove { if remote_path.exists() { fs::remove_dir_all(&remote_path)?; log::info!("Removed remote from modpack"); } else { log::warn!("No remote configured"); } return Ok(()); } // If no URL provided, show status if args.url.is_none() { show_remote_status(&remote_path); return Ok(()); } let url = args .url .ok_or_else(|| PakkerError::InvalidInput("URL is required".to_string()))?; log::info!("Installing modpack from: {url}"); // Clone or update repository if remote_path.exists() { log::info!("Remote directory exists, updating..."); let remote_name = "origin"; let ref_name = args.branch.as_deref().unwrap_or("HEAD"); git::fetch_updates(&remote_path, remote_name, ref_name, None)?; git::reset_to_ref(&remote_path, remote_name, ref_name)?; } else { log::info!("Cloning repository..."); let ref_name = args.branch.as_deref().unwrap_or("HEAD"); git::clone_repository(&url, &remote_path, ref_name, None)?; } // Load lockfile and config from remote let remote_lockfile_path = remote_path.join("pakku-lock.json"); if !remote_lockfile_path.exists() { return Err(PakkerError::ConfigError( "Remote repository does not contain pakku-lock.json".to_string(), )); } let remote_lockfile = LockFile::load(&remote_path)?; let remote_config = Config::load(&remote_path).ok(); // Copy lockfile to current directory let current_lockfile_path = PathBuf::from("pakku-lock.json"); fs::copy(&remote_lockfile_path, ¤t_lockfile_path)?; log::info!("Copied lockfile from remote"); // Copy config if exists if remote_config.is_some() { let remote_config_path = remote_path.join("pakku.json"); let current_config_path = PathBuf::from("pakku.json"); if remote_config_path.exists() { fs::copy(&remote_config_path, ¤t_config_path)?; log::info!("Copied config from remote"); } } // Fetch project files log::info!("Fetching project files..."); let fetcher = Fetcher::new(&remote_path); fetcher .fetch_all(&remote_lockfile, &remote_config.unwrap_or_default()) .await?; // Sync overrides sync_overrides(&remote_path, args.server_pack)?; log::info!("Successfully installed modpack from remote"); Ok(()) } fn show_remote_status(remote_path: &Path) { if !remote_path.exists() { println!("No remote configured"); return; } println!("Remote status:"); println!(" Directory: {}", remote_path.display()); if git::is_git_repository(remote_path) { if let Ok(url) = git::get_remote_url(remote_path, "origin") { println!(" URL: {url}"); } if let Ok(sha) = git::get_current_commit_sha(remote_path, None) { println!(" Commit: {}", &sha[..8]); } } } fn sync_overrides(remote_path: &Path, server_pack: bool) -> Result<()> { let override_dirs = if server_pack { vec!["overrides", "server_overrides"] } else { vec!["overrides", "client_overrides"] }; for dir_name in override_dirs { let src_dir = remote_path.join(dir_name); if src_dir.exists() && src_dir.is_dir() { log::info!("Syncing {dir_name} directory..."); copy_dir_recursive(&src_dir, Path::new("."))?; } } Ok(()) } fn copy_dir_recursive(src: &Path, dst: &Path) -> Result<()> { if !dst.exists() { fs::create_dir_all(dst)?; } for entry in fs::read_dir(src)? { let entry = entry?; let src_path = entry.path(); let file_name = entry.file_name(); let dst_path = dst.join(file_name); if src_path.is_dir() { copy_dir_recursive(&src_path, &dst_path)?; } else { fs::copy(&src_path, &dst_path)?; } } Ok(()) }