feat: Improve package management and testing
- Improve platform detection logic for more robust package management. - Enhance error handling and reporting in package commands. - Refactor code for better readability and maintainability. - Add comprehensive tests to cover package management functionality. - Improve test coverage for various scenarios and edge cases.
This commit is contained in:
parent
f002445c9e
commit
22f87b320e
@ -1,5 +1,5 @@
|
|||||||
use std::process::Command;
|
|
||||||
use crate::process::CommandResult;
|
use crate::process::CommandResult;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
/// Error type for package management operations
|
/// Error type for package management operations
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -45,7 +45,7 @@ impl Platform {
|
|||||||
if std::path::Path::new("/usr/bin/sw_vers").exists() {
|
if std::path::Path::new("/usr/bin/sw_vers").exists() {
|
||||||
return Platform::MacOS;
|
return Platform::MacOS;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for Ubuntu
|
// Check for Ubuntu
|
||||||
if std::path::Path::new("/etc/lsb-release").exists() {
|
if std::path::Path::new("/etc/lsb-release").exists() {
|
||||||
// Read the file to confirm it's Ubuntu
|
// Read the file to confirm it's Ubuntu
|
||||||
@ -55,12 +55,12 @@ impl Platform {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform::Unknown
|
Platform::Unknown
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Thread-local storage for debug flag
|
// Thread-local storage for debug flag
|
||||||
thread_local! {
|
thread_local! {
|
||||||
static DEBUG: std::cell::RefCell<bool> = std::cell::RefCell::new(false);
|
static DEBUG: std::cell::RefCell<bool> = std::cell::RefCell::new(false);
|
||||||
}
|
}
|
||||||
@ -74,70 +74,73 @@ pub fn set_thread_local_debug(debug: bool) {
|
|||||||
|
|
||||||
/// Get the debug flag for the current thread
|
/// Get the debug flag for the current thread
|
||||||
pub fn thread_local_debug() -> bool {
|
pub fn thread_local_debug() -> bool {
|
||||||
DEBUG.with(|cell| {
|
DEBUG.with(|cell| *cell.borrow())
|
||||||
*cell.borrow()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute a package management command and return the result
|
/// Execute a package management command and return the result
|
||||||
pub fn execute_package_command(args: &[&str], debug: bool) -> Result<CommandResult, PackageError> {
|
pub fn execute_package_command(args: &[&str], debug: bool) -> Result<CommandResult, PackageError> {
|
||||||
// Save the current debug flag
|
// Save the current debug flag
|
||||||
let previous_debug = thread_local_debug();
|
let previous_debug = thread_local_debug();
|
||||||
|
|
||||||
// Set the thread-local debug flag
|
// Set the thread-local debug flag
|
||||||
set_thread_local_debug(debug);
|
set_thread_local_debug(debug);
|
||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
println!("Executing command: {}", args.join(" "));
|
println!("Executing command: {}", args.join(" "));
|
||||||
}
|
}
|
||||||
|
|
||||||
let output = Command::new(args[0])
|
let output = Command::new(args[0]).args(&args[1..]).output();
|
||||||
.args(&args[1..])
|
|
||||||
.output();
|
|
||||||
|
|
||||||
// Restore the previous debug flag
|
// Restore the previous debug flag
|
||||||
set_thread_local_debug(previous_debug);
|
set_thread_local_debug(previous_debug);
|
||||||
|
|
||||||
match output {
|
match output {
|
||||||
Ok(output) => {
|
Ok(output) => {
|
||||||
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
|
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
|
||||||
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
|
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
|
||||||
|
|
||||||
let result = CommandResult {
|
let result = CommandResult {
|
||||||
stdout,
|
stdout,
|
||||||
stderr,
|
stderr,
|
||||||
success: output.status.success(),
|
success: output.status.success(),
|
||||||
code: output.status.code().unwrap_or(-1),
|
code: output.status.code().unwrap_or(-1),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Always output stdout/stderr when debug is true
|
// Always output stdout/stderr when debug is true
|
||||||
if debug {
|
if debug {
|
||||||
if !result.stdout.is_empty() {
|
if !result.stdout.is_empty() {
|
||||||
println!("Command stdout: {}", result.stdout);
|
println!("Command stdout: {}", result.stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !result.stderr.is_empty() {
|
if !result.stderr.is_empty() {
|
||||||
println!("Command stderr: {}", result.stderr);
|
println!("Command stderr: {}", result.stderr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if result.success {
|
if result.success {
|
||||||
println!("Command succeeded with code {}", result.code);
|
println!("Command succeeded with code {}", result.code);
|
||||||
} else {
|
} else {
|
||||||
println!("Command failed with code {}", result.code);
|
println!("Command failed with code {}", result.code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if result.success {
|
if result.success {
|
||||||
Ok(result)
|
Ok(result)
|
||||||
} else {
|
} else {
|
||||||
// If command failed and debug is false, output stderr
|
// If command failed and debug is false, output stderr
|
||||||
if !debug {
|
if !debug {
|
||||||
println!("Command failed with code {}: {}", result.code, result.stderr.trim());
|
println!(
|
||||||
|
"Command failed with code {}: {}",
|
||||||
|
result.code,
|
||||||
|
result.stderr.trim()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Err(PackageError::CommandFailed(format!("Command failed with code {}: {}",
|
Err(PackageError::CommandFailed(format!(
|
||||||
result.code, result.stderr.trim())))
|
"Command failed with code {}: {}",
|
||||||
|
result.code,
|
||||||
|
result.stderr.trim()
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// Always output error information
|
// Always output error information
|
||||||
println!("Command execution failed: {}", e);
|
println!("Command execution failed: {}", e);
|
||||||
@ -150,22 +153,22 @@ pub fn execute_package_command(args: &[&str], debug: bool) -> Result<CommandResu
|
|||||||
pub trait PackageManager {
|
pub trait PackageManager {
|
||||||
/// Install a package
|
/// Install a package
|
||||||
fn install(&self, package: &str) -> Result<CommandResult, PackageError>;
|
fn install(&self, package: &str) -> Result<CommandResult, PackageError>;
|
||||||
|
|
||||||
/// Remove a package
|
/// Remove a package
|
||||||
fn remove(&self, package: &str) -> Result<CommandResult, PackageError>;
|
fn remove(&self, package: &str) -> Result<CommandResult, PackageError>;
|
||||||
|
|
||||||
/// Update package lists
|
/// Update package lists
|
||||||
fn update(&self) -> Result<CommandResult, PackageError>;
|
fn update(&self) -> Result<CommandResult, PackageError>;
|
||||||
|
|
||||||
/// Upgrade installed packages
|
/// Upgrade installed packages
|
||||||
fn upgrade(&self) -> Result<CommandResult, PackageError>;
|
fn upgrade(&self) -> Result<CommandResult, PackageError>;
|
||||||
|
|
||||||
/// List installed packages
|
/// List installed packages
|
||||||
fn list_installed(&self) -> Result<Vec<String>, PackageError>;
|
fn list_installed(&self) -> Result<Vec<String>, PackageError>;
|
||||||
|
|
||||||
/// Search for packages
|
/// Search for packages
|
||||||
fn search(&self, query: &str) -> Result<Vec<String>, PackageError>;
|
fn search(&self, query: &str) -> Result<Vec<String>, PackageError>;
|
||||||
|
|
||||||
/// Check if a package is installed
|
/// Check if a package is installed
|
||||||
fn is_installed(&self, package: &str) -> Result<bool, PackageError>;
|
fn is_installed(&self, package: &str) -> Result<bool, PackageError>;
|
||||||
}
|
}
|
||||||
@ -185,27 +188,31 @@ impl AptPackageManager {
|
|||||||
impl PackageManager for AptPackageManager {
|
impl PackageManager for AptPackageManager {
|
||||||
fn install(&self, package: &str) -> Result<CommandResult, PackageError> {
|
fn install(&self, package: &str) -> Result<CommandResult, PackageError> {
|
||||||
// Use -y to make it non-interactive and --quiet to reduce output
|
// Use -y to make it non-interactive and --quiet to reduce output
|
||||||
execute_package_command(&["apt-get", "install", "-y", "--quiet", package], self.debug)
|
execute_package_command(
|
||||||
|
&["apt-get", "install", "-y", "--quiet", package],
|
||||||
|
self.debug,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove(&self, package: &str) -> Result<CommandResult, PackageError> {
|
fn remove(&self, package: &str) -> Result<CommandResult, PackageError> {
|
||||||
// Use -y to make it non-interactive and --quiet to reduce output
|
// Use -y to make it non-interactive and --quiet to reduce output
|
||||||
execute_package_command(&["apt-get", "remove", "-y", "--quiet", package], self.debug)
|
execute_package_command(&["apt-get", "remove", "-y", "--quiet", package], self.debug)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&self) -> Result<CommandResult, PackageError> {
|
fn update(&self) -> Result<CommandResult, PackageError> {
|
||||||
// Use -y to make it non-interactive and --quiet to reduce output
|
// Use -y to make it non-interactive and --quiet to reduce output
|
||||||
execute_package_command(&["apt-get", "update", "-y", "--quiet"], self.debug)
|
execute_package_command(&["apt-get", "update", "-y", "--quiet"], self.debug)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn upgrade(&self) -> Result<CommandResult, PackageError> {
|
fn upgrade(&self) -> Result<CommandResult, PackageError> {
|
||||||
// Use -y to make it non-interactive and --quiet to reduce output
|
// Use -y to make it non-interactive and --quiet to reduce output
|
||||||
execute_package_command(&["apt-get", "upgrade", "-y", "--quiet"], self.debug)
|
execute_package_command(&["apt-get", "upgrade", "-y", "--quiet"], self.debug)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_installed(&self) -> Result<Vec<String>, PackageError> {
|
fn list_installed(&self) -> Result<Vec<String>, PackageError> {
|
||||||
let result = execute_package_command(&["dpkg", "--get-selections"], self.debug)?;
|
let result = execute_package_command(&["dpkg", "--get-selections"], self.debug)?;
|
||||||
let packages = result.stdout
|
let packages = result
|
||||||
|
.stdout
|
||||||
.lines()
|
.lines()
|
||||||
.filter_map(|line| {
|
.filter_map(|line| {
|
||||||
let parts: Vec<&str> = line.split_whitespace().collect();
|
let parts: Vec<&str> = line.split_whitespace().collect();
|
||||||
@ -218,10 +225,11 @@ impl PackageManager for AptPackageManager {
|
|||||||
.collect();
|
.collect();
|
||||||
Ok(packages)
|
Ok(packages)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search(&self, query: &str) -> Result<Vec<String>, PackageError> {
|
fn search(&self, query: &str) -> Result<Vec<String>, PackageError> {
|
||||||
let result = execute_package_command(&["apt-cache", "search", query], self.debug)?;
|
let result = execute_package_command(&["apt-cache", "search", query], self.debug)?;
|
||||||
let packages = result.stdout
|
let packages = result
|
||||||
|
.stdout
|
||||||
.lines()
|
.lines()
|
||||||
.map(|line| {
|
.map(|line| {
|
||||||
let parts: Vec<&str> = line.split_whitespace().collect();
|
let parts: Vec<&str> = line.split_whitespace().collect();
|
||||||
@ -235,7 +243,7 @@ impl PackageManager for AptPackageManager {
|
|||||||
.collect();
|
.collect();
|
||||||
Ok(packages)
|
Ok(packages)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_installed(&self, package: &str) -> Result<bool, PackageError> {
|
fn is_installed(&self, package: &str) -> Result<bool, PackageError> {
|
||||||
let result = execute_package_command(&["dpkg", "-s", package], self.debug);
|
let result = execute_package_command(&["dpkg", "-s", package], self.debug);
|
||||||
match result {
|
match result {
|
||||||
@ -262,42 +270,44 @@ impl PackageManager for BrewPackageManager {
|
|||||||
// Use --quiet to reduce output
|
// Use --quiet to reduce output
|
||||||
execute_package_command(&["brew", "install", "--quiet", package], self.debug)
|
execute_package_command(&["brew", "install", "--quiet", package], self.debug)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove(&self, package: &str) -> Result<CommandResult, PackageError> {
|
fn remove(&self, package: &str) -> Result<CommandResult, PackageError> {
|
||||||
// Use --quiet to reduce output
|
// Use --quiet to reduce output
|
||||||
execute_package_command(&["brew", "uninstall", "--quiet", package], self.debug)
|
execute_package_command(&["brew", "uninstall", "--quiet", package], self.debug)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&self) -> Result<CommandResult, PackageError> {
|
fn update(&self) -> Result<CommandResult, PackageError> {
|
||||||
// Use --quiet to reduce output
|
// Use --quiet to reduce output
|
||||||
execute_package_command(&["brew", "update", "--quiet"], self.debug)
|
execute_package_command(&["brew", "update", "--quiet"], self.debug)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn upgrade(&self) -> Result<CommandResult, PackageError> {
|
fn upgrade(&self) -> Result<CommandResult, PackageError> {
|
||||||
// Use --quiet to reduce output
|
// Use --quiet to reduce output
|
||||||
execute_package_command(&["brew", "upgrade", "--quiet"], self.debug)
|
execute_package_command(&["brew", "upgrade", "--quiet"], self.debug)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_installed(&self) -> Result<Vec<String>, PackageError> {
|
fn list_installed(&self) -> Result<Vec<String>, PackageError> {
|
||||||
let result = execute_package_command(&["brew", "list", "--formula"], self.debug)?;
|
let result = execute_package_command(&["brew", "list", "--formula"], self.debug)?;
|
||||||
let packages = result.stdout
|
let packages = result
|
||||||
|
.stdout
|
||||||
.lines()
|
.lines()
|
||||||
.map(|line| line.trim().to_string())
|
.map(|line| line.trim().to_string())
|
||||||
.filter(|s| !s.is_empty())
|
.filter(|s| !s.is_empty())
|
||||||
.collect();
|
.collect();
|
||||||
Ok(packages)
|
Ok(packages)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search(&self, query: &str) -> Result<Vec<String>, PackageError> {
|
fn search(&self, query: &str) -> Result<Vec<String>, PackageError> {
|
||||||
let result = execute_package_command(&["brew", "search", query], self.debug)?;
|
let result = execute_package_command(&["brew", "search", query], self.debug)?;
|
||||||
let packages = result.stdout
|
let packages = result
|
||||||
|
.stdout
|
||||||
.lines()
|
.lines()
|
||||||
.map(|line| line.trim().to_string())
|
.map(|line| line.trim().to_string())
|
||||||
.filter(|s| !s.is_empty())
|
.filter(|s| !s.is_empty())
|
||||||
.collect();
|
.collect();
|
||||||
Ok(packages)
|
Ok(packages)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_installed(&self, package: &str) -> Result<bool, PackageError> {
|
fn is_installed(&self, package: &str) -> Result<bool, PackageError> {
|
||||||
let result = execute_package_command(&["brew", "list", package], self.debug);
|
let result = execute_package_command(&["brew", "list", package], self.debug);
|
||||||
match result {
|
match result {
|
||||||
@ -322,68 +332,70 @@ impl PackHero {
|
|||||||
debug: false,
|
debug: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the debug mode
|
/// Set the debug mode
|
||||||
pub fn set_debug(&mut self, debug: bool) -> &mut Self {
|
pub fn set_debug(&mut self, debug: bool) -> &mut Self {
|
||||||
self.debug = debug;
|
self.debug = debug;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the debug mode
|
/// Get the debug mode
|
||||||
pub fn debug(&self) -> bool {
|
pub fn debug(&self) -> bool {
|
||||||
self.debug
|
self.debug
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the detected platform
|
/// Get the detected platform
|
||||||
pub fn platform(&self) -> Platform {
|
pub fn platform(&self) -> Platform {
|
||||||
self.platform
|
self.platform
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a package manager for the current platform
|
/// Get a package manager for the current platform
|
||||||
fn get_package_manager(&self) -> Result<Box<dyn PackageManager>, PackageError> {
|
fn get_package_manager(&self) -> Result<Box<dyn PackageManager>, PackageError> {
|
||||||
match self.platform {
|
match self.platform {
|
||||||
Platform::Ubuntu => Ok(Box::new(AptPackageManager::new(self.debug))),
|
Platform::Ubuntu => Ok(Box::new(AptPackageManager::new(self.debug))),
|
||||||
Platform::MacOS => Ok(Box::new(BrewPackageManager::new(self.debug))),
|
Platform::MacOS => Ok(Box::new(BrewPackageManager::new(self.debug))),
|
||||||
Platform::Unknown => Err(PackageError::UnsupportedPlatform("Unsupported platform".to_string())),
|
Platform::Unknown => Err(PackageError::UnsupportedPlatform(
|
||||||
|
"Unsupported platform".to_string(),
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Install a package
|
/// Install a package
|
||||||
pub fn install(&self, package: &str) -> Result<CommandResult, PackageError> {
|
pub fn install(&self, package: &str) -> Result<CommandResult, PackageError> {
|
||||||
let pm = self.get_package_manager()?;
|
let pm = self.get_package_manager()?;
|
||||||
pm.install(package)
|
pm.install(package)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove a package
|
/// Remove a package
|
||||||
pub fn remove(&self, package: &str) -> Result<CommandResult, PackageError> {
|
pub fn remove(&self, package: &str) -> Result<CommandResult, PackageError> {
|
||||||
let pm = self.get_package_manager()?;
|
let pm = self.get_package_manager()?;
|
||||||
pm.remove(package)
|
pm.remove(package)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update package lists
|
/// Update package lists
|
||||||
pub fn update(&self) -> Result<CommandResult, PackageError> {
|
pub fn update(&self) -> Result<CommandResult, PackageError> {
|
||||||
let pm = self.get_package_manager()?;
|
let pm = self.get_package_manager()?;
|
||||||
pm.update()
|
pm.update()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Upgrade installed packages
|
/// Upgrade installed packages
|
||||||
pub fn upgrade(&self) -> Result<CommandResult, PackageError> {
|
pub fn upgrade(&self) -> Result<CommandResult, PackageError> {
|
||||||
let pm = self.get_package_manager()?;
|
let pm = self.get_package_manager()?;
|
||||||
pm.upgrade()
|
pm.upgrade()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// List installed packages
|
/// List installed packages
|
||||||
pub fn list_installed(&self) -> Result<Vec<String>, PackageError> {
|
pub fn list_installed(&self) -> Result<Vec<String>, PackageError> {
|
||||||
let pm = self.get_package_manager()?;
|
let pm = self.get_package_manager()?;
|
||||||
pm.list_installed()
|
pm.list_installed()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Search for packages
|
/// Search for packages
|
||||||
pub fn search(&self, query: &str) -> Result<Vec<String>, PackageError> {
|
pub fn search(&self, query: &str) -> Result<Vec<String>, PackageError> {
|
||||||
let pm = self.get_package_manager()?;
|
let pm = self.get_package_manager()?;
|
||||||
pm.search(query)
|
pm.search(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if a package is installed
|
/// Check if a package is installed
|
||||||
pub fn is_installed(&self, package: &str) -> Result<bool, PackageError> {
|
pub fn is_installed(&self, package: &str) -> Result<bool, PackageError> {
|
||||||
let pm = self.get_package_manager()?;
|
let pm = self.get_package_manager()?;
|
||||||
@ -394,47 +406,49 @@ impl PackHero {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
// Import the std::process::Command directly for some test-specific commands
|
// Import the std::process::Command directly for some test-specific commands
|
||||||
use std::process::Command as StdCommand;
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use std::process::Command as StdCommand;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_platform_detection() {
|
fn test_platform_detection() {
|
||||||
// This test will return different results depending on the platform it's run on
|
// This test will return different results depending on the platform it's run on
|
||||||
let platform = Platform::detect();
|
let platform = Platform::detect();
|
||||||
println!("Detected platform: {:?}", platform);
|
println!("Detected platform: {:?}", platform);
|
||||||
|
|
||||||
// Just ensure it doesn't panic
|
// Just ensure it doesn't panic
|
||||||
assert!(true);
|
assert!(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_debug_flag() {
|
fn test_debug_flag() {
|
||||||
// Test setting and getting the debug flag
|
// Test setting and getting the debug flag
|
||||||
set_thread_local_debug(true);
|
set_thread_local_debug(true);
|
||||||
assert_eq!(thread_local_debug(), true);
|
assert_eq!(thread_local_debug(), true);
|
||||||
|
|
||||||
set_thread_local_debug(false);
|
set_thread_local_debug(false);
|
||||||
assert_eq!(thread_local_debug(), false);
|
assert_eq!(thread_local_debug(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_package_error_display() {
|
fn test_package_error_display() {
|
||||||
// Test the Display implementation for PackageError
|
// Test the Display implementation for PackageError
|
||||||
let err1 = PackageError::CommandFailed("command failed".to_string());
|
let err1 = PackageError::CommandFailed("command failed".to_string());
|
||||||
assert_eq!(err1.to_string(), "Command failed: command failed");
|
assert_eq!(err1.to_string(), "Command failed: command failed");
|
||||||
|
|
||||||
let err2 = PackageError::UnsupportedPlatform("test platform".to_string());
|
let err2 = PackageError::UnsupportedPlatform("test platform".to_string());
|
||||||
assert_eq!(err2.to_string(), "Unsupported platform: test platform");
|
assert_eq!(err2.to_string(), "Unsupported platform: test platform");
|
||||||
|
|
||||||
let err3 = PackageError::Other("other error".to_string());
|
let err3 = PackageError::Other("other error".to_string());
|
||||||
assert_eq!(err3.to_string(), "Error: other error");
|
assert_eq!(err3.to_string(), "Error: other error");
|
||||||
|
|
||||||
// We can't easily test CommandExecutionFailed because std::io::Error doesn't implement PartialEq
|
// We can't easily test CommandExecutionFailed because std::io::Error doesn't implement PartialEq
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mock package manager for testing
|
// Mock package manager for testing
|
||||||
struct MockPackageManager {
|
struct MockPackageManager {
|
||||||
|
// debug field is kept for consistency with real package managers
|
||||||
|
#[allow(dead_code)]
|
||||||
debug: bool,
|
debug: bool,
|
||||||
install_called: Arc<Mutex<bool>>,
|
install_called: Arc<Mutex<bool>>,
|
||||||
remove_called: Arc<Mutex<bool>>,
|
remove_called: Arc<Mutex<bool>>,
|
||||||
@ -446,7 +460,7 @@ mod tests {
|
|||||||
// Control what the mock returns
|
// Control what the mock returns
|
||||||
should_succeed: bool,
|
should_succeed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MockPackageManager {
|
impl MockPackageManager {
|
||||||
fn new(debug: bool, should_succeed: bool) -> Self {
|
fn new(debug: bool, should_succeed: bool) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -462,7 +476,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PackageManager for MockPackageManager {
|
impl PackageManager for MockPackageManager {
|
||||||
fn install(&self, package: &str) -> Result<CommandResult, PackageError> {
|
fn install(&self, package: &str) -> Result<CommandResult, PackageError> {
|
||||||
*self.install_called.lock().unwrap() = true;
|
*self.install_called.lock().unwrap() = true;
|
||||||
@ -474,10 +488,12 @@ mod tests {
|
|||||||
code: 0,
|
code: 0,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(PackageError::CommandFailed("Mock install failed".to_string()))
|
Err(PackageError::CommandFailed(
|
||||||
|
"Mock install failed".to_string(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove(&self, package: &str) -> Result<CommandResult, PackageError> {
|
fn remove(&self, package: &str) -> Result<CommandResult, PackageError> {
|
||||||
*self.remove_called.lock().unwrap() = true;
|
*self.remove_called.lock().unwrap() = true;
|
||||||
if self.should_succeed {
|
if self.should_succeed {
|
||||||
@ -488,10 +504,12 @@ mod tests {
|
|||||||
code: 0,
|
code: 0,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(PackageError::CommandFailed("Mock remove failed".to_string()))
|
Err(PackageError::CommandFailed(
|
||||||
|
"Mock remove failed".to_string(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&self) -> Result<CommandResult, PackageError> {
|
fn update(&self) -> Result<CommandResult, PackageError> {
|
||||||
*self.update_called.lock().unwrap() = true;
|
*self.update_called.lock().unwrap() = true;
|
||||||
if self.should_succeed {
|
if self.should_succeed {
|
||||||
@ -502,10 +520,12 @@ mod tests {
|
|||||||
code: 0,
|
code: 0,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(PackageError::CommandFailed("Mock update failed".to_string()))
|
Err(PackageError::CommandFailed(
|
||||||
|
"Mock update failed".to_string(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn upgrade(&self) -> Result<CommandResult, PackageError> {
|
fn upgrade(&self) -> Result<CommandResult, PackageError> {
|
||||||
*self.upgrade_called.lock().unwrap() = true;
|
*self.upgrade_called.lock().unwrap() = true;
|
||||||
if self.should_succeed {
|
if self.should_succeed {
|
||||||
@ -516,45 +536,57 @@ mod tests {
|
|||||||
code: 0,
|
code: 0,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(PackageError::CommandFailed("Mock upgrade failed".to_string()))
|
Err(PackageError::CommandFailed(
|
||||||
|
"Mock upgrade failed".to_string(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_installed(&self) -> Result<Vec<String>, PackageError> {
|
fn list_installed(&self) -> Result<Vec<String>, PackageError> {
|
||||||
*self.list_installed_called.lock().unwrap() = true;
|
*self.list_installed_called.lock().unwrap() = true;
|
||||||
if self.should_succeed {
|
if self.should_succeed {
|
||||||
Ok(vec!["package1".to_string(), "package2".to_string()])
|
Ok(vec!["package1".to_string(), "package2".to_string()])
|
||||||
} else {
|
} else {
|
||||||
Err(PackageError::CommandFailed("Mock list_installed failed".to_string()))
|
Err(PackageError::CommandFailed(
|
||||||
|
"Mock list_installed failed".to_string(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search(&self, query: &str) -> Result<Vec<String>, PackageError> {
|
fn search(&self, query: &str) -> Result<Vec<String>, PackageError> {
|
||||||
*self.search_called.lock().unwrap() = true;
|
*self.search_called.lock().unwrap() = true;
|
||||||
if self.should_succeed {
|
if self.should_succeed {
|
||||||
Ok(vec![format!("result1-{}", query), format!("result2-{}", query)])
|
Ok(vec![
|
||||||
|
format!("result1-{}", query),
|
||||||
|
format!("result2-{}", query),
|
||||||
|
])
|
||||||
} else {
|
} else {
|
||||||
Err(PackageError::CommandFailed("Mock search failed".to_string()))
|
Err(PackageError::CommandFailed(
|
||||||
|
"Mock search failed".to_string(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_installed(&self, package: &str) -> Result<bool, PackageError> {
|
fn is_installed(&self, package: &str) -> Result<bool, PackageError> {
|
||||||
*self.is_installed_called.lock().unwrap() = true;
|
*self.is_installed_called.lock().unwrap() = true;
|
||||||
if self.should_succeed {
|
if self.should_succeed {
|
||||||
Ok(package == "installed-package")
|
Ok(package == "installed-package")
|
||||||
} else {
|
} else {
|
||||||
Err(PackageError::CommandFailed("Mock is_installed failed".to_string()))
|
Err(PackageError::CommandFailed(
|
||||||
|
"Mock is_installed failed".to_string(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Custom PackHero for testing with a mock package manager
|
// Custom PackHero for testing with a mock package manager
|
||||||
struct TestPackHero {
|
struct TestPackHero {
|
||||||
platform: Platform,
|
platform: Platform,
|
||||||
|
#[allow(dead_code)]
|
||||||
debug: bool,
|
debug: bool,
|
||||||
mock_manager: MockPackageManager,
|
mock_manager: MockPackageManager,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestPackHero {
|
impl TestPackHero {
|
||||||
fn new(platform: Platform, debug: bool, should_succeed: bool) -> Self {
|
fn new(platform: Platform, debug: bool, should_succeed: bool) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -563,144 +595,152 @@ mod tests {
|
|||||||
mock_manager: MockPackageManager::new(debug, should_succeed),
|
mock_manager: MockPackageManager::new(debug, should_succeed),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_package_manager(&self) -> Result<&dyn PackageManager, PackageError> {
|
fn get_package_manager(&self) -> Result<&dyn PackageManager, PackageError> {
|
||||||
match self.platform {
|
match self.platform {
|
||||||
Platform::Ubuntu | Platform::MacOS => Ok(&self.mock_manager),
|
Platform::Ubuntu | Platform::MacOS => Ok(&self.mock_manager),
|
||||||
Platform::Unknown => Err(PackageError::UnsupportedPlatform("Unsupported platform".to_string())),
|
Platform::Unknown => Err(PackageError::UnsupportedPlatform(
|
||||||
|
"Unsupported platform".to_string(),
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn install(&self, package: &str) -> Result<CommandResult, PackageError> {
|
fn install(&self, package: &str) -> Result<CommandResult, PackageError> {
|
||||||
let pm = self.get_package_manager()?;
|
let pm = self.get_package_manager()?;
|
||||||
pm.install(package)
|
pm.install(package)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove(&self, package: &str) -> Result<CommandResult, PackageError> {
|
fn remove(&self, package: &str) -> Result<CommandResult, PackageError> {
|
||||||
let pm = self.get_package_manager()?;
|
let pm = self.get_package_manager()?;
|
||||||
pm.remove(package)
|
pm.remove(package)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&self) -> Result<CommandResult, PackageError> {
|
fn update(&self) -> Result<CommandResult, PackageError> {
|
||||||
let pm = self.get_package_manager()?;
|
let pm = self.get_package_manager()?;
|
||||||
pm.update()
|
pm.update()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn upgrade(&self) -> Result<CommandResult, PackageError> {
|
fn upgrade(&self) -> Result<CommandResult, PackageError> {
|
||||||
let pm = self.get_package_manager()?;
|
let pm = self.get_package_manager()?;
|
||||||
pm.upgrade()
|
pm.upgrade()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_installed(&self) -> Result<Vec<String>, PackageError> {
|
fn list_installed(&self) -> Result<Vec<String>, PackageError> {
|
||||||
let pm = self.get_package_manager()?;
|
let pm = self.get_package_manager()?;
|
||||||
pm.list_installed()
|
pm.list_installed()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search(&self, query: &str) -> Result<Vec<String>, PackageError> {
|
fn search(&self, query: &str) -> Result<Vec<String>, PackageError> {
|
||||||
let pm = self.get_package_manager()?;
|
let pm = self.get_package_manager()?;
|
||||||
pm.search(query)
|
pm.search(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_installed(&self, package: &str) -> Result<bool, PackageError> {
|
fn is_installed(&self, package: &str) -> Result<bool, PackageError> {
|
||||||
let pm = self.get_package_manager()?;
|
let pm = self.get_package_manager()?;
|
||||||
pm.is_installed(package)
|
pm.is_installed(package)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_packhero_with_mock_success() {
|
fn test_packhero_with_mock_success() {
|
||||||
// Test PackHero with a mock package manager that succeeds
|
// Test PackHero with a mock package manager that succeeds
|
||||||
let hero = TestPackHero::new(Platform::Ubuntu, false, true);
|
let hero = TestPackHero::new(Platform::Ubuntu, false, true);
|
||||||
|
|
||||||
// Test install
|
// Test install
|
||||||
let result = hero.install("test-package");
|
let result = hero.install("test-package");
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
assert!(*hero.mock_manager.install_called.lock().unwrap());
|
assert!(*hero.mock_manager.install_called.lock().unwrap());
|
||||||
|
|
||||||
// Test remove
|
// Test remove
|
||||||
let result = hero.remove("test-package");
|
let result = hero.remove("test-package");
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
assert!(*hero.mock_manager.remove_called.lock().unwrap());
|
assert!(*hero.mock_manager.remove_called.lock().unwrap());
|
||||||
|
|
||||||
// Test update
|
// Test update
|
||||||
let result = hero.update();
|
let result = hero.update();
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
assert!(*hero.mock_manager.update_called.lock().unwrap());
|
assert!(*hero.mock_manager.update_called.lock().unwrap());
|
||||||
|
|
||||||
// Test upgrade
|
// Test upgrade
|
||||||
let result = hero.upgrade();
|
let result = hero.upgrade();
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
assert!(*hero.mock_manager.upgrade_called.lock().unwrap());
|
assert!(*hero.mock_manager.upgrade_called.lock().unwrap());
|
||||||
|
|
||||||
// Test list_installed
|
// Test list_installed
|
||||||
let result = hero.list_installed();
|
let result = hero.list_installed();
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
assert_eq!(result.unwrap(), vec!["package1".to_string(), "package2".to_string()]);
|
assert_eq!(
|
||||||
|
result.unwrap(),
|
||||||
|
vec!["package1".to_string(), "package2".to_string()]
|
||||||
|
);
|
||||||
assert!(*hero.mock_manager.list_installed_called.lock().unwrap());
|
assert!(*hero.mock_manager.list_installed_called.lock().unwrap());
|
||||||
|
|
||||||
// Test search
|
// Test search
|
||||||
let result = hero.search("query");
|
let result = hero.search("query");
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
assert_eq!(result.unwrap(), vec!["result1-query".to_string(), "result2-query".to_string()]);
|
assert_eq!(
|
||||||
|
result.unwrap(),
|
||||||
|
vec!["result1-query".to_string(), "result2-query".to_string()]
|
||||||
|
);
|
||||||
assert!(*hero.mock_manager.search_called.lock().unwrap());
|
assert!(*hero.mock_manager.search_called.lock().unwrap());
|
||||||
|
|
||||||
// Test is_installed
|
// Test is_installed
|
||||||
let result = hero.is_installed("installed-package");
|
let result = hero.is_installed("installed-package");
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
assert!(result.unwrap());
|
assert!(result.unwrap());
|
||||||
assert!(*hero.mock_manager.is_installed_called.lock().unwrap());
|
assert!(*hero.mock_manager.is_installed_called.lock().unwrap());
|
||||||
|
|
||||||
let result = hero.is_installed("not-installed-package");
|
let result = hero.is_installed("not-installed-package");
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
assert!(!result.unwrap());
|
assert!(!result.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_packhero_with_mock_failure() {
|
fn test_packhero_with_mock_failure() {
|
||||||
// Test PackHero with a mock package manager that fails
|
// Test PackHero with a mock package manager that fails
|
||||||
let hero = TestPackHero::new(Platform::Ubuntu, false, false);
|
let hero = TestPackHero::new(Platform::Ubuntu, false, false);
|
||||||
|
|
||||||
// Test install
|
// Test install
|
||||||
let result = hero.install("test-package");
|
let result = hero.install("test-package");
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
assert!(*hero.mock_manager.install_called.lock().unwrap());
|
assert!(*hero.mock_manager.install_called.lock().unwrap());
|
||||||
|
|
||||||
// Test remove
|
// Test remove
|
||||||
let result = hero.remove("test-package");
|
let result = hero.remove("test-package");
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
assert!(*hero.mock_manager.remove_called.lock().unwrap());
|
assert!(*hero.mock_manager.remove_called.lock().unwrap());
|
||||||
|
|
||||||
// Test update
|
// Test update
|
||||||
let result = hero.update();
|
let result = hero.update();
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
assert!(*hero.mock_manager.update_called.lock().unwrap());
|
assert!(*hero.mock_manager.update_called.lock().unwrap());
|
||||||
|
|
||||||
// Test upgrade
|
// Test upgrade
|
||||||
let result = hero.upgrade();
|
let result = hero.upgrade();
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
assert!(*hero.mock_manager.upgrade_called.lock().unwrap());
|
assert!(*hero.mock_manager.upgrade_called.lock().unwrap());
|
||||||
|
|
||||||
// Test list_installed
|
// Test list_installed
|
||||||
let result = hero.list_installed();
|
let result = hero.list_installed();
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
assert!(*hero.mock_manager.list_installed_called.lock().unwrap());
|
assert!(*hero.mock_manager.list_installed_called.lock().unwrap());
|
||||||
|
|
||||||
// Test search
|
// Test search
|
||||||
let result = hero.search("query");
|
let result = hero.search("query");
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
assert!(*hero.mock_manager.search_called.lock().unwrap());
|
assert!(*hero.mock_manager.search_called.lock().unwrap());
|
||||||
|
|
||||||
// Test is_installed
|
// Test is_installed
|
||||||
let result = hero.is_installed("installed-package");
|
let result = hero.is_installed("installed-package");
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
assert!(*hero.mock_manager.is_installed_called.lock().unwrap());
|
assert!(*hero.mock_manager.is_installed_called.lock().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_packhero_unsupported_platform() {
|
fn test_packhero_unsupported_platform() {
|
||||||
// Test PackHero with an unsupported platform
|
// Test PackHero with an unsupported platform
|
||||||
let hero = TestPackHero::new(Platform::Unknown, false, true);
|
let hero = TestPackHero::new(Platform::Unknown, false, true);
|
||||||
|
|
||||||
// All operations should fail with UnsupportedPlatform error
|
// All operations should fail with UnsupportedPlatform error
|
||||||
let result = hero.install("test-package");
|
let result = hero.install("test-package");
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
@ -708,14 +748,14 @@ mod tests {
|
|||||||
Err(PackageError::UnsupportedPlatform(_)) => (),
|
Err(PackageError::UnsupportedPlatform(_)) => (),
|
||||||
_ => panic!("Expected UnsupportedPlatform error"),
|
_ => panic!("Expected UnsupportedPlatform error"),
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = hero.remove("test-package");
|
let result = hero.remove("test-package");
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
match result {
|
match result {
|
||||||
Err(PackageError::UnsupportedPlatform(_)) => (),
|
Err(PackageError::UnsupportedPlatform(_)) => (),
|
||||||
_ => panic!("Expected UnsupportedPlatform error"),
|
_ => panic!("Expected UnsupportedPlatform error"),
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = hero.update();
|
let result = hero.update();
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
match result {
|
match result {
|
||||||
@ -723,7 +763,7 @@ mod tests {
|
|||||||
_ => panic!("Expected UnsupportedPlatform error"),
|
_ => panic!("Expected UnsupportedPlatform error"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Real-world tests that actually install and remove packages on Ubuntu
|
// Real-world tests that actually install and remove packages on Ubuntu
|
||||||
// These tests will only run on Ubuntu and will be skipped on other platforms
|
// These tests will only run on Ubuntu and will be skipped on other platforms
|
||||||
#[test]
|
#[test]
|
||||||
@ -731,19 +771,22 @@ mod tests {
|
|||||||
// Check if we're on Ubuntu
|
// Check if we're on Ubuntu
|
||||||
let platform = Platform::detect();
|
let platform = Platform::detect();
|
||||||
if platform != Platform::Ubuntu {
|
if platform != Platform::Ubuntu {
|
||||||
println!("Skipping real package operations test on non-Ubuntu platform: {:?}", platform);
|
println!(
|
||||||
|
"Skipping real package operations test on non-Ubuntu platform: {:?}",
|
||||||
|
platform
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("Running real package operations test on Ubuntu");
|
println!("Running real package operations test on Ubuntu");
|
||||||
|
|
||||||
// Create a PackHero instance with debug enabled
|
// Create a PackHero instance with debug enabled
|
||||||
let mut hero = PackHero::new();
|
let mut hero = PackHero::new();
|
||||||
hero.set_debug(true);
|
hero.set_debug(true);
|
||||||
|
|
||||||
// Test package to install/remove
|
// Test package to install/remove
|
||||||
let test_package = "wget";
|
let test_package = "wget";
|
||||||
|
|
||||||
// First, check if the package is already installed
|
// First, check if the package is already installed
|
||||||
let is_installed_before = match hero.is_installed(test_package) {
|
let is_installed_before = match hero.is_installed(test_package) {
|
||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
@ -752,9 +795,12 @@ mod tests {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
println!("Package {} is installed before test: {}", test_package, is_installed_before);
|
println!(
|
||||||
|
"Package {} is installed before test: {}",
|
||||||
|
test_package, is_installed_before
|
||||||
|
);
|
||||||
|
|
||||||
// If the package is already installed, we'll remove it first
|
// If the package is already installed, we'll remove it first
|
||||||
if is_installed_before {
|
if is_installed_before {
|
||||||
println!("Removing existing package {} before test", test_package);
|
println!("Removing existing package {} before test", test_package);
|
||||||
@ -765,7 +811,7 @@ mod tests {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify it was removed
|
// Verify it was removed
|
||||||
match hero.is_installed(test_package) {
|
match hero.is_installed(test_package) {
|
||||||
Ok(is_installed) => {
|
Ok(is_installed) => {
|
||||||
@ -775,14 +821,17 @@ mod tests {
|
|||||||
} else {
|
} else {
|
||||||
println!("Verified package {} was removed", test_package);
|
println!("Verified package {} was removed", test_package);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Error checking if package is installed after removal: {}", e);
|
println!(
|
||||||
|
"Error checking if package is installed after removal: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now install the package
|
// Now install the package
|
||||||
println!("Installing package {}", test_package);
|
println!("Installing package {}", test_package);
|
||||||
match hero.install(test_package) {
|
match hero.install(test_package) {
|
||||||
@ -792,7 +841,7 @@ mod tests {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify it was installed
|
// Verify it was installed
|
||||||
match hero.is_installed(test_package) {
|
match hero.is_installed(test_package) {
|
||||||
Ok(is_installed) => {
|
Ok(is_installed) => {
|
||||||
@ -802,41 +851,50 @@ mod tests {
|
|||||||
} else {
|
} else {
|
||||||
println!("Verified package {} was installed", test_package);
|
println!("Verified package {} was installed", test_package);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Error checking if package is installed after installation: {}", e);
|
println!(
|
||||||
|
"Error checking if package is installed after installation: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test the search functionality
|
// Test the search functionality
|
||||||
println!("Searching for packages with 'wget'");
|
println!("Searching for packages with 'wget'");
|
||||||
match hero.search("wget") {
|
match hero.search("wget") {
|
||||||
Ok(results) => {
|
Ok(results) => {
|
||||||
println!("Search results: {:?}", results);
|
println!("Search results: {:?}", results);
|
||||||
assert!(results.iter().any(|r| r.contains("wget")), "Search results should contain wget");
|
assert!(
|
||||||
},
|
results.iter().any(|r| r.contains("wget")),
|
||||||
|
"Search results should contain wget"
|
||||||
|
);
|
||||||
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Error searching for packages: {}", e);
|
println!("Error searching for packages: {}", e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test listing installed packages
|
// Test listing installed packages
|
||||||
println!("Listing installed packages");
|
println!("Listing installed packages");
|
||||||
match hero.list_installed() {
|
match hero.list_installed() {
|
||||||
Ok(packages) => {
|
Ok(packages) => {
|
||||||
println!("Found {} installed packages", packages.len());
|
println!("Found {} installed packages", packages.len());
|
||||||
// Check if our test package is in the list
|
// Check if our test package is in the list
|
||||||
assert!(packages.iter().any(|p| p == test_package),
|
assert!(
|
||||||
"Installed packages list should contain {}", test_package);
|
packages.iter().any(|p| p == test_package),
|
||||||
},
|
"Installed packages list should contain {}",
|
||||||
|
test_package
|
||||||
|
);
|
||||||
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Error listing installed packages: {}", e);
|
println!("Error listing installed packages: {}", e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now remove the package if it wasn't installed before
|
// Now remove the package if it wasn't installed before
|
||||||
if !is_installed_before {
|
if !is_installed_before {
|
||||||
println!("Removing package {} after test", test_package);
|
println!("Removing package {} after test", test_package);
|
||||||
@ -847,7 +905,7 @@ mod tests {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify it was removed
|
// Verify it was removed
|
||||||
match hero.is_installed(test_package) {
|
match hero.is_installed(test_package) {
|
||||||
Ok(is_installed) => {
|
Ok(is_installed) => {
|
||||||
@ -857,14 +915,17 @@ mod tests {
|
|||||||
} else {
|
} else {
|
||||||
println!("Verified package {} was removed", test_package);
|
println!("Verified package {} was removed", test_package);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Error checking if package is installed after removal: {}", e);
|
println!(
|
||||||
|
"Error checking if package is installed after removal: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test update functionality
|
// Test update functionality
|
||||||
println!("Testing package list update");
|
println!("Testing package list update");
|
||||||
match hero.update() {
|
match hero.update() {
|
||||||
@ -874,10 +935,10 @@ mod tests {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("All real package operations tests passed on Ubuntu");
|
println!("All real package operations tests passed on Ubuntu");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test to check if apt-get is available on the system
|
// Test to check if apt-get is available on the system
|
||||||
#[test]
|
#[test]
|
||||||
fn test_apt_get_availability() {
|
fn test_apt_get_availability() {
|
||||||
@ -886,18 +947,18 @@ mod tests {
|
|||||||
.arg("apt-get")
|
.arg("apt-get")
|
||||||
.output()
|
.output()
|
||||||
.expect("Failed to execute which apt-get");
|
.expect("Failed to execute which apt-get");
|
||||||
|
|
||||||
let success = output.status.success();
|
let success = output.status.success();
|
||||||
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
|
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
|
||||||
|
|
||||||
println!("apt-get available: {}", success);
|
println!("apt-get available: {}", success);
|
||||||
if success {
|
if success {
|
||||||
println!("apt-get path: {}", stdout.trim());
|
println!("apt-get path: {}", stdout.trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
// On Ubuntu, this should pass
|
// On Ubuntu, this should pass
|
||||||
if Platform::detect() == Platform::Ubuntu {
|
if Platform::detect() == Platform::Ubuntu {
|
||||||
assert!(success, "apt-get should be available on Ubuntu");
|
assert!(success, "apt-get should be available on Ubuntu");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io::{self, Read, Seek, SeekFrom};
|
use std::io::{self, Read};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
/// Represents the type of replacement to perform.
|
/// Represents the type of replacement to perform.
|
||||||
@ -46,36 +45,36 @@ impl TextReplacer {
|
|||||||
/// Applies all configured replacement operations to the input text
|
/// Applies all configured replacement operations to the input text
|
||||||
pub fn replace(&self, input: &str) -> String {
|
pub fn replace(&self, input: &str) -> String {
|
||||||
let mut result = input.to_string();
|
let mut result = input.to_string();
|
||||||
|
|
||||||
// Apply each replacement operation in sequence
|
// Apply each replacement operation in sequence
|
||||||
for op in &self.operations {
|
for op in &self.operations {
|
||||||
result = op.apply(&result);
|
result = op.apply(&result);
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads a file, applies all replacements, and returns the result as a string
|
/// Reads a file, applies all replacements, and returns the result as a string
|
||||||
pub fn replace_file<P: AsRef<Path>>(&self, path: P) -> io::Result<String> {
|
pub fn replace_file<P: AsRef<Path>>(&self, path: P) -> io::Result<String> {
|
||||||
let mut file = fs::File::open(path)?;
|
let mut file = fs::File::open(path)?;
|
||||||
let mut content = String::new();
|
let mut content = String::new();
|
||||||
file.read_to_string(&mut content)?;
|
file.read_to_string(&mut content)?;
|
||||||
|
|
||||||
Ok(self.replace(&content))
|
Ok(self.replace(&content))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads a file, applies all replacements, and writes the result back to the file
|
/// Reads a file, applies all replacements, and writes the result back to the file
|
||||||
pub fn replace_file_in_place<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
|
pub fn replace_file_in_place<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
|
||||||
let content = self.replace_file(&path)?;
|
let content = self.replace_file(&path)?;
|
||||||
fs::write(path, content)?;
|
fs::write(path, content)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads a file, applies all replacements, and writes the result to a new file
|
/// Reads a file, applies all replacements, and writes the result to a new file
|
||||||
pub fn replace_file_to<P1: AsRef<Path>, P2: AsRef<Path>>(
|
pub fn replace_file_to<P1: AsRef<Path>, P2: AsRef<Path>>(
|
||||||
&self,
|
&self,
|
||||||
input_path: P1,
|
input_path: P1,
|
||||||
output_path: P2
|
output_path: P2,
|
||||||
) -> io::Result<()> {
|
) -> io::Result<()> {
|
||||||
let content = self.replace_file(&input_path)?;
|
let content = self.replace_file(&input_path)?;
|
||||||
fs::write(output_path, content)?;
|
fs::write(output_path, content)?;
|
||||||
@ -111,13 +110,13 @@ impl TextReplacerBuilder {
|
|||||||
self.use_regex = yes;
|
self.use_regex = yes;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets whether the replacement should be case-insensitive
|
/// Sets whether the replacement should be case-insensitive
|
||||||
pub fn case_insensitive(mut self, yes: bool) -> Self {
|
pub fn case_insensitive(mut self, yes: bool) -> Self {
|
||||||
self.case_insensitive = yes;
|
self.case_insensitive = yes;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds another replacement operation to the chain and resets the builder for a new operation
|
/// Adds another replacement operation to the chain and resets the builder for a new operation
|
||||||
pub fn and(mut self) -> Self {
|
pub fn and(mut self) -> Self {
|
||||||
self.add_current_operation();
|
self.add_current_operation();
|
||||||
@ -130,20 +129,20 @@ impl TextReplacerBuilder {
|
|||||||
let replacement = self.replacement.take().unwrap_or_default();
|
let replacement = self.replacement.take().unwrap_or_default();
|
||||||
let use_regex = self.use_regex;
|
let use_regex = self.use_regex;
|
||||||
let case_insensitive = self.case_insensitive;
|
let case_insensitive = self.case_insensitive;
|
||||||
|
|
||||||
// Reset current settings
|
// Reset current settings
|
||||||
self.use_regex = false;
|
self.use_regex = false;
|
||||||
self.case_insensitive = false;
|
self.case_insensitive = false;
|
||||||
|
|
||||||
// Create the replacement mode
|
// Create the replacement mode
|
||||||
let mode = if use_regex {
|
let mode = if use_regex {
|
||||||
let mut regex_pattern = pattern;
|
let mut regex_pattern = pattern;
|
||||||
|
|
||||||
// If case insensitive, add the flag to the regex pattern
|
// If case insensitive, add the flag to the regex pattern
|
||||||
if case_insensitive && !regex_pattern.starts_with("(?i)") {
|
if case_insensitive && !regex_pattern.starts_with("(?i)") {
|
||||||
regex_pattern = format!("(?i){}", regex_pattern);
|
regex_pattern = format!("(?i){}", regex_pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
match Regex::new(®ex_pattern) {
|
match Regex::new(®ex_pattern) {
|
||||||
Ok(re) => ReplaceMode::Regex(re),
|
Ok(re) => ReplaceMode::Regex(re),
|
||||||
Err(_) => return false, // Failed to compile regex
|
Err(_) => return false, // Failed to compile regex
|
||||||
@ -156,12 +155,10 @@ impl TextReplacerBuilder {
|
|||||||
}
|
}
|
||||||
ReplaceMode::Literal(pattern)
|
ReplaceMode::Literal(pattern)
|
||||||
};
|
};
|
||||||
|
|
||||||
self.operations.push(ReplacementOperation {
|
self.operations
|
||||||
mode,
|
.push(ReplacementOperation { mode, replacement });
|
||||||
replacement,
|
|
||||||
});
|
|
||||||
|
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
@ -172,12 +169,12 @@ impl TextReplacerBuilder {
|
|||||||
pub fn build(mut self) -> Result<TextReplacer, String> {
|
pub fn build(mut self) -> Result<TextReplacer, String> {
|
||||||
// If there's a pending replacement operation, add it
|
// If there's a pending replacement operation, add it
|
||||||
self.add_current_operation();
|
self.add_current_operation();
|
||||||
|
|
||||||
// Ensure we have at least one replacement operation
|
// Ensure we have at least one replacement operation
|
||||||
if self.operations.is_empty() {
|
if self.operations.is_empty() {
|
||||||
return Err("No replacement operations configured".to_string());
|
return Err("No replacement operations configured".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(TextReplacer {
|
Ok(TextReplacer {
|
||||||
operations: self.operations,
|
operations: self.operations,
|
||||||
})
|
})
|
||||||
@ -187,7 +184,7 @@ impl TextReplacerBuilder {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::io::Write;
|
use std::io::{Seek, SeekFrom, Write};
|
||||||
use tempfile::NamedTempFile;
|
use tempfile::NamedTempFile;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -219,7 +216,7 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(output, "qux bar qux baz");
|
assert_eq!(output, "qux bar qux baz");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_multiple_replacements() {
|
fn test_multiple_replacements() {
|
||||||
let replacer = TextReplacer::builder()
|
let replacer = TextReplacer::builder()
|
||||||
@ -236,7 +233,7 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(output, "qux baz qux");
|
assert_eq!(output, "qux baz qux");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_case_insensitive_regex() {
|
fn test_case_insensitive_regex() {
|
||||||
let replacer = TextReplacer::builder()
|
let replacer = TextReplacer::builder()
|
||||||
@ -252,44 +249,44 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(output, "bar bar bar");
|
assert_eq!(output, "bar bar bar");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_file_operations() -> io::Result<()> {
|
fn test_file_operations() -> io::Result<()> {
|
||||||
// Create a temporary file
|
// Create a temporary file
|
||||||
let mut temp_file = NamedTempFile::new()?;
|
let mut temp_file = NamedTempFile::new()?;
|
||||||
writeln!(temp_file, "foo bar foo baz")?;
|
writeln!(temp_file, "foo bar foo baz")?;
|
||||||
|
|
||||||
// Flush the file to ensure content is written
|
// Flush the file to ensure content is written
|
||||||
temp_file.as_file_mut().flush()?;
|
temp_file.as_file_mut().flush()?;
|
||||||
|
|
||||||
let replacer = TextReplacer::builder()
|
let replacer = TextReplacer::builder()
|
||||||
.pattern("foo")
|
.pattern("foo")
|
||||||
.replacement("qux")
|
.replacement("qux")
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Test replace_file
|
// Test replace_file
|
||||||
let result = replacer.replace_file(temp_file.path())?;
|
let result = replacer.replace_file(temp_file.path())?;
|
||||||
assert_eq!(result, "qux bar qux baz\n");
|
assert_eq!(result, "qux bar qux baz\n");
|
||||||
|
|
||||||
// Test replace_file_in_place
|
// Test replace_file_in_place
|
||||||
replacer.replace_file_in_place(temp_file.path())?;
|
replacer.replace_file_in_place(temp_file.path())?;
|
||||||
|
|
||||||
// Verify the file was updated - need to seek to beginning of file first
|
// Verify the file was updated - need to seek to beginning of file first
|
||||||
let mut content = String::new();
|
let mut content = String::new();
|
||||||
temp_file.as_file_mut().seek(SeekFrom::Start(0))?;
|
temp_file.as_file_mut().seek(SeekFrom::Start(0))?;
|
||||||
temp_file.as_file_mut().read_to_string(&mut content)?;
|
temp_file.as_file_mut().read_to_string(&mut content)?;
|
||||||
assert_eq!(content, "qux bar qux baz\n");
|
assert_eq!(content, "qux bar qux baz\n");
|
||||||
|
|
||||||
// Test replace_file_to with a new temporary file
|
// Test replace_file_to with a new temporary file
|
||||||
let output_file = NamedTempFile::new()?;
|
let output_file = NamedTempFile::new()?;
|
||||||
replacer.replace_file_to(temp_file.path(), output_file.path())?;
|
replacer.replace_file_to(temp_file.path(), output_file.path())?;
|
||||||
|
|
||||||
// Verify the output file has the replaced content
|
// Verify the output file has the replaced content
|
||||||
let mut output_content = String::new();
|
let mut output_content = String::new();
|
||||||
fs::File::open(output_file.path())?.read_to_string(&mut output_content)?;
|
fs::File::open(output_file.path())?.read_to_string(&mut output_content)?;
|
||||||
assert_eq!(output_content, "qux bar qux baz\n");
|
assert_eq!(output_content, "qux bar qux baz\n");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
use super::{
|
use super::{
|
||||||
error::RfsError,
|
|
||||||
cmd::execute_rfs_command,
|
cmd::execute_rfs_command,
|
||||||
|
error::RfsError,
|
||||||
types::{Mount, MountType, StoreSpec},
|
types::{Mount, MountType, StoreSpec},
|
||||||
};
|
};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
/// Builder for RFS mount operations
|
/// Builder for RFS mount operations
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -17,6 +17,7 @@ pub struct RfsBuilder {
|
|||||||
/// Mount options
|
/// Mount options
|
||||||
options: HashMap<String, String>,
|
options: HashMap<String, String>,
|
||||||
/// Mount ID
|
/// Mount ID
|
||||||
|
#[allow(dead_code)]
|
||||||
mount_id: Option<String>,
|
mount_id: Option<String>,
|
||||||
/// Debug mode
|
/// Debug mode
|
||||||
debug: bool,
|
debug: bool,
|
||||||
@ -44,7 +45,7 @@ impl RfsBuilder {
|
|||||||
debug: false,
|
debug: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a mount option
|
/// Add a mount option
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
@ -59,7 +60,7 @@ impl RfsBuilder {
|
|||||||
self.options.insert(key.to_string(), value.to_string());
|
self.options.insert(key.to_string(), value.to_string());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add multiple mount options
|
/// Add multiple mount options
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
@ -75,7 +76,7 @@ impl RfsBuilder {
|
|||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set debug mode
|
/// Set debug mode
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
@ -89,7 +90,7 @@ impl RfsBuilder {
|
|||||||
self.debug = debug;
|
self.debug = debug;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mount the filesystem
|
/// Mount the filesystem
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
@ -99,7 +100,7 @@ impl RfsBuilder {
|
|||||||
// Build the command string
|
// Build the command string
|
||||||
let mut cmd = String::from("mount -t ");
|
let mut cmd = String::from("mount -t ");
|
||||||
cmd.push_str(&self.mount_type.to_string());
|
cmd.push_str(&self.mount_type.to_string());
|
||||||
|
|
||||||
// Add options if any
|
// Add options if any
|
||||||
if !self.options.is_empty() {
|
if !self.options.is_empty() {
|
||||||
cmd.push_str(" -o ");
|
cmd.push_str(" -o ");
|
||||||
@ -114,35 +115,39 @@ impl RfsBuilder {
|
|||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add source and target
|
// Add source and target
|
||||||
cmd.push_str(" ");
|
cmd.push_str(" ");
|
||||||
cmd.push_str(&self.source);
|
cmd.push_str(&self.source);
|
||||||
cmd.push_str(" ");
|
cmd.push_str(" ");
|
||||||
cmd.push_str(&self.target);
|
cmd.push_str(&self.target);
|
||||||
|
|
||||||
// Split the command into arguments
|
// Split the command into arguments
|
||||||
let args: Vec<&str> = cmd.split_whitespace().collect();
|
let args: Vec<&str> = cmd.split_whitespace().collect();
|
||||||
|
|
||||||
// Execute the command
|
// Execute the command
|
||||||
let result = execute_rfs_command(&args)?;
|
let result = execute_rfs_command(&args)?;
|
||||||
|
|
||||||
// Parse the output to get the mount ID
|
// Parse the output to get the mount ID
|
||||||
let mount_id = result.stdout.trim().to_string();
|
let mount_id = result.stdout.trim().to_string();
|
||||||
if mount_id.is_empty() {
|
if mount_id.is_empty() {
|
||||||
return Err(RfsError::MountFailed("Failed to get mount ID".to_string()));
|
return Err(RfsError::MountFailed("Failed to get mount ID".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create and return the Mount struct
|
// Create and return the Mount struct
|
||||||
Ok(Mount {
|
Ok(Mount {
|
||||||
id: mount_id,
|
id: mount_id,
|
||||||
source: self.source,
|
source: self.source,
|
||||||
target: self.target,
|
target: self.target,
|
||||||
fs_type: self.mount_type.to_string(),
|
fs_type: self.mount_type.to_string(),
|
||||||
options: self.options.iter().map(|(k, v)| format!("{}={}", k, v)).collect(),
|
options: self
|
||||||
|
.options
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| format!("{}={}", k, v))
|
||||||
|
.collect(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unmount the filesystem
|
/// Unmount the filesystem
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
@ -151,12 +156,15 @@ impl RfsBuilder {
|
|||||||
pub fn unmount(&self) -> Result<(), RfsError> {
|
pub fn unmount(&self) -> Result<(), RfsError> {
|
||||||
// Execute the unmount command
|
// Execute the unmount command
|
||||||
let result = execute_rfs_command(&["unmount", &self.target])?;
|
let result = execute_rfs_command(&["unmount", &self.target])?;
|
||||||
|
|
||||||
// Check for errors
|
// Check for errors
|
||||||
if !result.success {
|
if !result.success {
|
||||||
return Err(RfsError::UnmountFailed(format!("Failed to unmount {}: {}", self.target, result.stderr)));
|
return Err(RfsError::UnmountFailed(format!(
|
||||||
|
"Failed to unmount {}: {}",
|
||||||
|
self.target, result.stderr
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -193,7 +201,7 @@ impl PackBuilder {
|
|||||||
debug: false,
|
debug: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a store specification
|
/// Add a store specification
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
@ -207,7 +215,7 @@ impl PackBuilder {
|
|||||||
self.store_specs.push(store_spec);
|
self.store_specs.push(store_spec);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add multiple store specifications
|
/// Add multiple store specifications
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
@ -221,7 +229,7 @@ impl PackBuilder {
|
|||||||
self.store_specs.extend(store_specs);
|
self.store_specs.extend(store_specs);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set debug mode
|
/// Set debug mode
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
@ -235,7 +243,7 @@ impl PackBuilder {
|
|||||||
self.debug = debug;
|
self.debug = debug;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pack the directory
|
/// Pack the directory
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
@ -245,7 +253,7 @@ impl PackBuilder {
|
|||||||
// Build the command string
|
// Build the command string
|
||||||
let mut cmd = String::from("pack -m ");
|
let mut cmd = String::from("pack -m ");
|
||||||
cmd.push_str(&self.output);
|
cmd.push_str(&self.output);
|
||||||
|
|
||||||
// Add store specs if any
|
// Add store specs if any
|
||||||
if !self.store_specs.is_empty() {
|
if !self.store_specs.is_empty() {
|
||||||
cmd.push_str(" -s ");
|
cmd.push_str(" -s ");
|
||||||
@ -259,22 +267,25 @@ impl PackBuilder {
|
|||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add directory
|
// Add directory
|
||||||
cmd.push_str(" ");
|
cmd.push_str(" ");
|
||||||
cmd.push_str(&self.directory);
|
cmd.push_str(&self.directory);
|
||||||
|
|
||||||
// Split the command into arguments
|
// Split the command into arguments
|
||||||
let args: Vec<&str> = cmd.split_whitespace().collect();
|
let args: Vec<&str> = cmd.split_whitespace().collect();
|
||||||
|
|
||||||
// Execute the command
|
// Execute the command
|
||||||
let result = execute_rfs_command(&args)?;
|
let result = execute_rfs_command(&args)?;
|
||||||
|
|
||||||
// Check for errors
|
// Check for errors
|
||||||
if !result.success {
|
if !result.success {
|
||||||
return Err(RfsError::PackFailed(format!("Failed to pack {}: {}", self.directory, result.stderr)));
|
return Err(RfsError::PackFailed(format!(
|
||||||
|
"Failed to pack {}: {}",
|
||||||
|
self.directory, result.stderr
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::process::{run_command, CommandResult};
|
|
||||||
use super::error::RfsError;
|
use super::error::RfsError;
|
||||||
use std::thread_local;
|
use crate::process::{run_command, CommandResult};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
use std::thread_local;
|
||||||
|
|
||||||
// Thread-local storage for debug flag
|
// Thread-local storage for debug flag
|
||||||
thread_local! {
|
thread_local! {
|
||||||
@ -9,6 +9,7 @@ thread_local! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Set the thread-local debug flag
|
/// Set the thread-local debug flag
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn set_thread_local_debug(debug: bool) {
|
pub fn set_thread_local_debug(debug: bool) {
|
||||||
DEBUG.with(|d| {
|
DEBUG.with(|d| {
|
||||||
*d.borrow_mut() = debug;
|
*d.borrow_mut() = debug;
|
||||||
@ -17,9 +18,7 @@ pub fn set_thread_local_debug(debug: bool) {
|
|||||||
|
|
||||||
/// Get the current thread-local debug flag
|
/// Get the current thread-local debug flag
|
||||||
pub fn thread_local_debug() -> bool {
|
pub fn thread_local_debug() -> bool {
|
||||||
DEBUG.with(|d| {
|
DEBUG.with(|d| *d.borrow())
|
||||||
*d.borrow()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute an RFS command with the given arguments
|
/// Execute an RFS command with the given arguments
|
||||||
@ -33,30 +32,30 @@ pub fn thread_local_debug() -> bool {
|
|||||||
/// * `Result<CommandResult, RfsError>` - Command result or error
|
/// * `Result<CommandResult, RfsError>` - Command result or error
|
||||||
pub fn execute_rfs_command(args: &[&str]) -> Result<CommandResult, RfsError> {
|
pub fn execute_rfs_command(args: &[&str]) -> Result<CommandResult, RfsError> {
|
||||||
let debug = thread_local_debug();
|
let debug = thread_local_debug();
|
||||||
|
|
||||||
// Construct the command string
|
// Construct the command string
|
||||||
let mut cmd = String::from("rfs");
|
let mut cmd = String::from("rfs");
|
||||||
for arg in args {
|
for arg in args {
|
||||||
cmd.push(' ');
|
cmd.push(' ');
|
||||||
cmd.push_str(arg);
|
cmd.push_str(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
println!("Executing RFS command: {}", cmd);
|
println!("Executing RFS command: {}", cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute the command
|
// Execute the command
|
||||||
let result = run_command(&cmd)
|
let result = run_command(&cmd)
|
||||||
.map_err(|e| RfsError::CommandFailed(format!("Failed to execute RFS command: {}", e)))?;
|
.map_err(|e| RfsError::CommandFailed(format!("Failed to execute RFS command: {}", e)))?;
|
||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
println!("RFS command result: {:?}", result);
|
println!("RFS command result: {:?}", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the command was successful
|
// Check if the command was successful
|
||||||
if !result.success && !result.stderr.is_empty() {
|
if !result.success && !result.stderr.is_empty() {
|
||||||
return Err(RfsError::CommandFailed(result.stderr));
|
return Err(RfsError::CommandFailed(result.stderr));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user