//! Package management commands for apt-ostree use crate::commands::Command; use apt_ostree::lib::error::{AptOstreeError, AptOstreeResult}; use apt_ostree::lib::ostree::OstreeManager; /// Install command - Overlay additional packages pub struct InstallCommand; impl InstallCommand { pub fn new() -> Self { Self } } impl Command for InstallCommand { fn execute(&self, args: &[String]) -> AptOstreeResult<()> { if args.contains(&"--help".to_string()) || args.contains(&"-h".to_string()) { self.show_help(); return Ok(()); } if args.is_empty() { return Err(AptOstreeError::InvalidArgument( "No packages specified. Use --help for usage information.".to_string() )); } // Parse options let mut opt_dry_run = false; let mut opt_verbose = false; let mut opt_no_deps = false; let packages: Vec = args.iter() .filter(|arg| !arg.starts_with('-')) .cloned() .collect(); for arg in args { match arg.as_str() { "--dry-run" | "-n" => opt_dry_run = true, "--verbose" | "-v" => opt_verbose = true, "--no-deps" => opt_no_deps = true, "--help" | "-h" => { self.show_help(); return Ok(()); } _ => {} } } if packages.is_empty() { return Err(AptOstreeError::InvalidArgument( "No packages specified. Use --help for usage information.".to_string() )); } println!("📦 Install Packages"); println!("==================="); println!("Packages to install: {}", packages.join(", ")); if opt_dry_run { println!("Mode: Dry run (no actual installation)"); } if opt_verbose { println!("Mode: Verbose output"); } if opt_no_deps { println!("Mode: No dependency installation"); } println!(); // Use the real APT manager for installation use apt_ostree::lib::apt::AptManager; let apt_manager = AptManager::new(); // Check if APT is available if !apt_manager.check_database_health()? { return Err(AptOstreeError::System("APT database is not healthy".to_string())); } if opt_dry_run { println!("Dry run mode - would install the following packages:"); for package in &packages { if let Ok(Some(pkg_info)) = apt_manager.get_package_info(package) { println!(" {} (version: {})", pkg_info.name, pkg_info.version); println!(" Description: {}", pkg_info.description); if !pkg_info.depends.is_empty() { println!(" Dependencies: {}", pkg_info.depends.join(", ")); } println!(); } else { println!(" {} - Package not found", package); } } println!("Dry run completed. No packages were actually installed."); return Ok(()); } // Install packages for package in &packages { println!("Installing package: {}", package); // Since install_package is async, we'll use a simple approach for now // TODO: Make the Command trait async or use a different approach match apt_manager.install_package(package) { Ok(_) => println!("Successfully installed: {}", package), Err(e) => { println!("Failed to install {}: {}", package, e); return Err(e); } } } println!(); println!("✅ All packages installed successfully!"); println!("Note: On OSTree systems, packages are installed as overlays"); println!(" and will persist across system updates."); Ok(()) } fn name(&self) -> &'static str { "install" } fn description(&self) -> &'static str { "Overlay additional packages" } fn show_help(&self) { println!("apt-ostree install - Overlay additional packages"); println!(); println!("Usage: apt-ostree install ... [OPTIONS]"); println!(); println!("Arguments:"); println!(" PACKAGES Package names to install"); println!(); println!("Options:"); println!(" --dry-run, -n Show what would be installed without actually installing"); println!(" --verbose, -v Show detailed output during installation"); println!(" --no-deps Skip dependency installation (not recommended)"); println!(" --help, -h Show this help message"); println!(); println!("Examples:"); println!(" apt-ostree install nginx"); println!(" apt-ostree install nginx vim htop"); println!(" apt-ostree install --dry-run nginx"); println!(" apt-ostree install --verbose nginx"); println!(); println!("Note: On OSTree systems, packages are installed as overlays"); println!(" and will persist across system updates."); } } /// Uninstall command - Remove overlayed additional packages pub struct UninstallCommand; impl UninstallCommand { pub fn new() -> Self { Self } } impl Command for UninstallCommand { fn execute(&self, args: &[String]) -> AptOstreeResult<()> { if args.contains(&"--help".to_string()) || args.contains(&"-h".to_string()) { self.show_help(); return Ok(()); } if args.is_empty() { return Err(AptOstreeError::InvalidArgument( "No packages specified. Use --help for usage information.".to_string() )); } let packages: Vec = args.iter() .filter(|arg| !arg.starts_with('-')) .cloned() .collect(); if packages.is_empty() { return Err(AptOstreeError::InvalidArgument( "No packages specified. Use --help for usage information.".to_string() )); } println!("🗑️ Uninstall Packages"); println!("====================="); println!("Packages to remove: {}", packages.join(", ")); println!(); // Check if we're on an OSTree system let ostree_manager = OstreeManager::new(); let is_ostree_system = ostree_manager.is_available() && ostree_manager.is_ostree_booted(); if is_ostree_system { println!("OSTree: System is booted from OSTree"); println!("Mode: Package overlay removal"); } else { println!("OSTree: Traditional package management system"); println!("Mode: Standard package removal"); } println!(); // Use the real APT manager for package removal use apt_ostree::lib::apt::AptManager; let apt_manager = AptManager::new(); // Check if APT is available if !apt_manager.check_database_health()? { return Err(AptOstreeError::System("APT database is not healthy".to_string())); } // Process each package let mut success_count = 0; let mut failure_count = 0; for package in &packages { println!("Removing package: {}", package); // Check if package is installed if !apt_manager.is_package_installed(package)? { println!(" Warning: Package '{}' is not installed", package); continue; } // Get package info before removal if let Ok(Some(pkg_info)) = apt_manager.get_package_info(package) { println!(" Package: {} (version: {})", pkg_info.name, pkg_info.version); println!(" Description: {}", pkg_info.description); // Check for reverse dependencies // TODO: Implement reverse dependency checking println!(" Checking dependencies..."); } // Remove the package match apt_manager.remove_package(package) { Ok(_) => { println!(" ✅ Successfully removed: {}", package); success_count += 1; } Err(e) => { println!(" ❌ Failed to remove {}: {}", package, e); failure_count += 1; } } println!(); } // Summary println!("Uninstall Summary:"); println!(" Successfully removed: {} packages", success_count); if failure_count > 0 { println!(" Failed to remove: {} packages", failure_count); } if is_ostree_system { println!(); println!("Note: On OSTree systems, package overlays have been removed."); println!(" The base system remains unchanged."); } if failure_count == 0 { println!("✅ All packages removed successfully!"); } else { println!("⚠️ Some packages could not be removed. Check the output above."); } Ok(()) } fn name(&self) -> &'static str { "uninstall" } fn description(&self) -> &'static str { "Remove overlayed additional packages" } fn show_help(&self) { println!("apt-ostree uninstall - Remove overlayed additional packages"); println!(); println!("Usage: apt-ostree uninstall ... [OPTIONS]"); println!(); println!("Arguments:"); println!(" PACKAGES Package names to remove"); println!(); println!("Options:"); println!(" --help, -h Show this help message"); } } /// Search command - Search for packages pub struct SearchCommand; impl SearchCommand { pub fn new() -> Self { Self } } impl Command for SearchCommand { fn execute(&self, args: &[String]) -> AptOstreeResult<()> { if args.contains(&"--help".to_string()) || args.contains(&"-h".to_string()) { self.show_help(); return Ok(()); } if args.is_empty() { return Err(AptOstreeError::InvalidArgument( "No search query specified. Use --help for usage information.".to_string() )); } // Parse options let mut opt_exact = false; let mut opt_regex = false; let mut opt_verbose = false; let mut search_query = String::new(); let mut i = 0; while i < args.len() { match args[i].as_str() { "--exact" | "-e" => opt_exact = true, "--regex" | "-r" => opt_regex = true, "--verbose" | "-v" => opt_verbose = true, "--help" | "-h" => { self.show_help(); return Ok(()); } arg if !arg.starts_with('-') => { search_query = arg.to_string(); } _ => {} } i += 1; } if search_query.is_empty() { return Err(AptOstreeError::InvalidArgument( "No search query specified. Use --help for usage information.".to_string() )); } println!("🔍 Package Search"); println!("================="); println!("Query: {}", search_query); println!("Mode: {}", if opt_exact { "Exact Match" } else if opt_regex { "Regex" } else { "Standard Search" }); println!(); // Use the real APT manager for search use apt_ostree::lib::apt::AptManager; let apt_manager = AptManager::new(); let packages = if opt_exact { apt_manager.search_packages_exact(&search_query)? } else if opt_regex { apt_manager.search_packages_regex(&search_query)? } else { apt_manager.search_packages(&search_query)? }; if packages.is_empty() { println!("No packages found matching '{}'", search_query); return Ok(()); } println!("Found {} packages:", packages.len()); println!(); for package in packages { let status = if package.installed { "✓" } else { " " }; println!("{} {} - {}", status, package.name, package.description); if opt_verbose { println!(" Version: {}", package.version); println!(" Section: {}", package.section); println!(" Priority: {}", package.priority); if !package.depends.is_empty() { println!(" Dependencies: {}", package.depends.join(", ")); } println!(); } } Ok(()) } fn name(&self) -> &'static str { "search" } fn description(&self) -> &'static str { "Search for packages" } fn show_help(&self) { println!("apt-ostree search - Search for packages"); println!(); println!("Usage: apt-ostree search [OPTIONS]"); println!(); println!("Arguments:"); println!(" QUERY Search query (package name or description)"); println!(); println!("Options:"); println!(" --exact, -e Exact package name match"); println!(" --regex, -r Regular expression search"); println!(" --verbose, -v Show detailed package information"); println!(" --help, -h Show this help message"); println!(); println!("Examples:"); println!(" apt-ostree search nginx"); println!(" apt-ostree search --exact nginx"); println!(" apt-ostree search --regex '^nginx.*'"); println!(" apt-ostree search --verbose nginx"); } }