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}