This commit is contained in:
2025-04-05 07:23:07 +02:00
parent 6de7bf9b56
commit 6d4c1742e7
14 changed files with 533 additions and 455 deletions

View File

@@ -14,6 +14,7 @@ pub enum FsError {
CopyFailed(io::Error),
DeleteFailed(io::Error),
CommandFailed(String),
CommandNotFound(String),
CommandExecutionError(io::Error),
InvalidGlobPattern(glob::PatternError),
NotADirectory(String),
@@ -36,6 +37,7 @@ impl fmt::Display for FsError {
FsError::CopyFailed(e) => write!(f, "Failed to copy file: {}", e),
FsError::DeleteFailed(e) => write!(f, "Failed to delete: {}", e),
FsError::CommandFailed(e) => write!(f, "{}", e),
FsError::CommandNotFound(e) => write!(f, "Command not found: {}", e),
FsError::CommandExecutionError(e) => write!(f, "Failed to execute command: {}", e),
FsError::InvalidGlobPattern(e) => write!(f, "Invalid glob pattern: {}", e),
FsError::NotADirectory(path) => write!(f, "Path '{}' exists but is not a directory", path),
@@ -740,3 +742,105 @@ pub fn file_write_append(path: &str, content: &str) -> Result<String, FsError> {
Ok(format!("Successfully appended to file '{}'", path))
}
/**
* Check if a command exists in the system PATH.
*
* # Arguments
*
* * `command` - The command to check
*
* # Returns
*
* * `String` - Empty string if the command doesn't exist, path to the command if it does
*
* # Examples
*
* ```
* let cmd_path = which("ls");
* if cmd_path != "" {
* println!("ls is available at: {}", cmd_path);
* }
* ```
*/
pub fn which(command: &str) -> String {
// Use the appropriate command based on the platform
#[cfg(target_os = "windows")]
let output = Command::new("where")
.arg(command)
.output();
#[cfg(not(target_os = "windows"))]
let output = Command::new("which")
.arg(command)
.output();
match output {
Ok(out) => {
if out.status.success() {
let path = String::from_utf8_lossy(&out.stdout).trim().to_string();
path
} else {
String::new()
}
},
Err(_) => String::new(),
}
}
/**
* Ensure that one or more commands exist in the system PATH.
* If any command doesn't exist, an error is thrown.
*
* # Arguments
*
* * `commands` - The command(s) to check, comma-separated for multiple commands
*
* # Returns
*
* * `Ok(String)` - A success message indicating all commands exist
* * `Err(FsError)` - An error if any command doesn't exist
*
* # Examples
*
* ```
* // Check if a single command exists
* let result = cmd_ensure_exists("nerdctl")?;
*
* // Check if multiple commands exist
* let result = cmd_ensure_exists("nerdctl,docker,containerd")?;
* ```
*/
pub fn cmd_ensure_exists(commands: &str) -> Result<String, FsError> {
// Split the input by commas to handle multiple commands
let command_list: Vec<&str> = commands.split(',')
.map(|s| s.trim())
.filter(|s| !s.is_empty())
.collect();
if command_list.is_empty() {
return Err(FsError::CommandFailed("No commands specified to check".to_string()));
}
let mut missing_commands = Vec::new();
// Check each command
for cmd in &command_list {
let cmd_path = which(cmd);
if cmd_path.is_empty() {
missing_commands.push(cmd.to_string());
}
}
// If any commands are missing, return an error
if !missing_commands.is_empty() {
return Err(FsError::CommandNotFound(missing_commands.join(", ")));
}
// All commands exist
if command_list.len() == 1 {
Ok(format!("Command '{}' exists", command_list[0]))
} else {
Ok(format!("All commands exist: {}", command_list.join(", ")))
}
}