use std::process::Command; use crate::process::CommandResult; /// Error type for package management operations #[derive(Debug)] pub enum PackageError { /// Command failed with error message CommandFailed(String), /// Command execution failed with IO error CommandExecutionFailed(std::io::Error), /// Unsupported platform UnsupportedPlatform(String), /// Other error Other(String), } impl std::fmt::Display for PackageError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { PackageError::CommandFailed(msg) => write!(f, "Command failed: {}", msg), PackageError::CommandExecutionFailed(e) => write!(f, "Command execution failed: {}", e), PackageError::UnsupportedPlatform(msg) => write!(f, "Unsupported platform: {}", msg), PackageError::Other(msg) => write!(f, "Error: {}", msg), } } } impl std::error::Error for PackageError {} /// Platform enum for detecting the current operating system #[derive(Debug, Clone, Copy, PartialEq)] pub enum Platform { /// Ubuntu Linux Ubuntu, /// macOS MacOS, /// Unknown platform Unknown, } impl Platform { /// Detect the current platform pub fn detect() -> Self { // Check for macOS if std::path::Path::new("/usr/bin/sw_vers").exists() { return Platform::MacOS; } // Check for Ubuntu if std::path::Path::new("/etc/lsb-release").exists() { // Read the file to confirm it's Ubuntu if let Ok(content) = std::fs::read_to_string("/etc/lsb-release") { if content.contains("Ubuntu") { return Platform::Ubuntu; } } } Platform::Unknown } } /// Thread-local storage for debug flag thread_local! { static DEBUG: std::cell::RefCell = std::cell::RefCell::new(false); } /// Set the debug flag for the current thread pub fn set_thread_local_debug(debug: bool) { DEBUG.with(|cell| { *cell.borrow_mut() = debug; }); } /// Get the debug flag for the current thread pub fn thread_local_debug() -> bool { DEBUG.with(|cell| { *cell.borrow() }) } /// Execute a package management command and return the result pub fn execute_package_command(args: &[&str], debug: bool) -> Result { // Save the current debug flag let previous_debug = thread_local_debug(); // Set the thread-local debug flag set_thread_local_debug(debug); if debug { println!("Executing command: {}", args.join(" ")); } let output = Command::new(args[0]) .args(&args[1..]) .output(); // Restore the previous debug flag set_thread_local_debug(previous_debug); match output { Ok(output) => { let stdout = String::from_utf8_lossy(&output.stdout).to_string(); let stderr = String::from_utf8_lossy(&output.stderr).to_string(); let result = CommandResult { stdout, stderr, success: output.status.success(), code: output.status.code().unwrap_or(-1), }; // Always output stdout/stderr when debug is true if debug { if !result.stdout.is_empty() { println!("Command stdout: {}", result.stdout); } if !result.stderr.is_empty() { println!("Command stderr: {}", result.stderr); } if result.success { println!("Command succeeded with code {}", result.code); } else { println!("Command failed with code {}", result.code); } } if result.success { Ok(result) } else { // If command failed and debug is false, output stderr if !debug { println!("Command failed with code {}: {}", result.code, result.stderr.trim()); } Err(PackageError::CommandFailed(format!("Command failed with code {}: {}", result.code, result.stderr.trim()))) } }, Err(e) => { // Always output error information println!("Command execution failed: {}", e); Err(PackageError::CommandExecutionFailed(e)) } } } /// Trait for package managers pub trait PackageManager { /// Install a package fn install(&self, package: &str) -> Result; /// Remove a package fn remove(&self, package: &str) -> Result; /// Update package lists fn update(&self) -> Result; /// Upgrade installed packages fn upgrade(&self) -> Result; /// List installed packages fn list_installed(&self) -> Result, PackageError>; /// Search for packages fn search(&self, query: &str) -> Result, PackageError>; /// Check if a package is installed fn is_installed(&self, package: &str) -> Result; } /// APT package manager for Ubuntu pub struct AptPackageManager { debug: bool, } impl AptPackageManager { /// Create a new APT package manager pub fn new(debug: bool) -> Self { Self { debug } } } impl PackageManager for AptPackageManager { fn install(&self, package: &str) -> Result { // Use -y to make it non-interactive and --quiet to reduce output execute_package_command(&["apt-get", "install", "-y", "--quiet", package], self.debug) } fn remove(&self, package: &str) -> Result { // Use -y to make it non-interactive and --quiet to reduce output execute_package_command(&["apt-get", "remove", "-y", "--quiet", package], self.debug) } fn update(&self) -> Result { // Use -y to make it non-interactive and --quiet to reduce output execute_package_command(&["apt-get", "update", "-y", "--quiet"], self.debug) } fn upgrade(&self) -> Result { // Use -y to make it non-interactive and --quiet to reduce output execute_package_command(&["apt-get", "upgrade", "-y", "--quiet"], self.debug) } fn list_installed(&self) -> Result, PackageError> { let result = execute_package_command(&["dpkg", "--get-selections"], self.debug)?; let packages = result.stdout .lines() .filter_map(|line| { let parts: Vec<&str> = line.split_whitespace().collect(); if parts.len() >= 2 && parts[1] == "install" { Some(parts[0].to_string()) } else { None } }) .collect(); Ok(packages) } fn search(&self, query: &str) -> Result, PackageError> { let result = execute_package_command(&["apt-cache", "search", query], self.debug)?; let packages = result.stdout .lines() .map(|line| { let parts: Vec<&str> = line.split_whitespace().collect(); if !parts.is_empty() { parts[0].to_string() } else { String::new() } }) .filter(|s| !s.is_empty()) .collect(); Ok(packages) } fn is_installed(&self, package: &str) -> Result { let result = execute_package_command(&["dpkg", "-s", package], self.debug); match result { Ok(cmd_result) => Ok(cmd_result.success), Err(_) => Ok(false), } } } /// Homebrew package manager for macOS pub struct BrewPackageManager { debug: bool, } impl BrewPackageManager { /// Create a new Homebrew package manager pub fn new(debug: bool) -> Self { Self { debug } } } impl PackageManager for BrewPackageManager { fn install(&self, package: &str) -> Result { // Use --quiet to reduce output execute_package_command(&["brew", "install", "--quiet", package], self.debug) } fn remove(&self, package: &str) -> Result { // Use --quiet to reduce output execute_package_command(&["brew", "uninstall", "--quiet", package], self.debug) } fn update(&self) -> Result { // Use --quiet to reduce output execute_package_command(&["brew", "update", "--quiet"], self.debug) } fn upgrade(&self) -> Result { // Use --quiet to reduce output execute_package_command(&["brew", "upgrade", "--quiet"], self.debug) } fn list_installed(&self) -> Result, PackageError> { let result = execute_package_command(&["brew", "list", "--formula"], self.debug)?; let packages = result.stdout .lines() .map(|line| line.trim().to_string()) .filter(|s| !s.is_empty()) .collect(); Ok(packages) } fn search(&self, query: &str) -> Result, PackageError> { let result = execute_package_command(&["brew", "search", query], self.debug)?; let packages = result.stdout .lines() .map(|line| line.trim().to_string()) .filter(|s| !s.is_empty()) .collect(); Ok(packages) } fn is_installed(&self, package: &str) -> Result { let result = execute_package_command(&["brew", "list", package], self.debug); match result { Ok(cmd_result) => Ok(cmd_result.success), Err(_) => Ok(false), } } } /// PackHero factory for package management pub struct PackHero { platform: Platform, debug: bool, } impl PackHero { /// Create a new PackHero instance pub fn new() -> Self { let platform = Platform::detect(); Self { platform, debug: false, } } /// Set the debug mode pub fn set_debug(&mut self, debug: bool) -> &mut Self { self.debug = debug; self } /// Get the debug mode pub fn debug(&self) -> bool { self.debug } /// Get the detected platform pub fn platform(&self) -> Platform { self.platform } /// Get a package manager for the current platform fn get_package_manager(&self) -> Result, PackageError> { match self.platform { Platform::Ubuntu => Ok(Box::new(AptPackageManager::new(self.debug))), Platform::MacOS => Ok(Box::new(BrewPackageManager::new(self.debug))), Platform::Unknown => Err(PackageError::UnsupportedPlatform("Unsupported platform".to_string())), } } /// Install a package pub fn install(&self, package: &str) -> Result { let pm = self.get_package_manager()?; pm.install(package) } /// Remove a package pub fn remove(&self, package: &str) -> Result { let pm = self.get_package_manager()?; pm.remove(package) } /// Update package lists pub fn update(&self) -> Result { let pm = self.get_package_manager()?; pm.update() } /// Upgrade installed packages pub fn upgrade(&self) -> Result { let pm = self.get_package_manager()?; pm.upgrade() } /// List installed packages pub fn list_installed(&self) -> Result, PackageError> { let pm = self.get_package_manager()?; pm.list_installed() } /// Search for packages pub fn search(&self, query: &str) -> Result, PackageError> { let pm = self.get_package_manager()?; pm.search(query) } /// Check if a package is installed pub fn is_installed(&self, package: &str) -> Result { let pm = self.get_package_manager()?; pm.is_installed(package) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_platform_detection() { // This test will return different results depending on the platform it's run on let platform = Platform::detect(); println!("Detected platform: {:?}", platform); // Just ensure it doesn't panic assert!(true); } #[test] fn test_debug_flag() { // Test setting and getting the debug flag set_thread_local_debug(true); assert_eq!(thread_local_debug(), true); set_thread_local_debug(false); assert_eq!(thread_local_debug(), false); } // More tests would be added for each platform-specific implementation // These would likely be integration tests that are conditionally compiled // based on the platform they're running on }