easy_cmd/
functions.rs

1use crate::traits::AsCommand;
2use std::path::Path;
3use std::process::Command;
4
5/// Runs a system command.
6///
7/// # Arguments
8///
9/// * `cmd` - The command to run. The command can be represented as a single string or a collection
10///   of strings, or as any other type that implements the [`crate::AsCommand`] trait.
11/// * `dir` - Path to the directory that the command specified by `cmd` should be run in (can be a
12///   `&str`, [`String`], [`Path`], or [`std::path::PathBuf`].). If [`None`], the command is run in
13///   the current working directory.
14///
15/// # Returns
16///
17/// A result where:
18///
19/// * `Ok` contains `()` if the command executes successfully (exit status zero).
20/// * `Err` contains an error message if the command fails to run or exits with a non-zero status.
21///
22/// # Example
23///
24/// ```
25/// use easy_cmd::run_command;
26/// use std::path::Path;
27///
28/// // Run `git status` inside the current working directory (the `easy_cmd` repo).
29/// //  --> Note: we have to disambiguate the `None` type to `None::<&str>` since the compiler
30/// //      cannot infer the type of the path.
31/// run_command("git status", None::<&str>).unwrap();
32///
33/// // Alternatively, we could run the same command using a collection of strings.
34/// //  --> Note: like before, we have to disambiguate the `None` type to `None::<&str>` since the
35/// //      compile cannot infer the type of the path.
36/// run_command(&["git", "status"], None::<&str>).unwrap();
37///
38/// // If we want to run `ls -la` in the `src` directory, we can do it like this:
39/// run_command("ls -la", Some("src")).unwrap();
40///
41/// // Alternatively, we could run the same command using a a collection of strings for the command
42/// // and a `Path` for the directory:
43/// run_command(&vec!["ls", "-la"], Some(Path::new("src"))).unwrap();
44/// ```
45pub fn run_command<C: AsCommand + ?Sized, P: AsRef<Path>>(
46    cmd: &C,
47    dir: Option<P>,
48) -> Result<(), String> {
49    // Create the Command object with the first element as the command name.
50    let mut command: Command = cmd.as_command();
51
52    // Set the current directory if provided.
53    if let Some(dir_path) = dir {
54        command.current_dir(dir_path);
55    }
56
57    // Execute the command and capture the exit status.
58    let status = command
59        .status()
60        .map_err(|e| format!("Failed to run command {command:?}: {e}"))?;
61
62    // Check if the command exited successfully.
63    if status.success() {
64        Ok(())
65    } else {
66        Err(format!(
67            "Command {command:?} failed with status: {status:?}"
68        ))
69    }
70}