support_kit/
shell.rs

1use crate::ShellCommandError;
2
3#[derive(Clone, Debug)]
4pub struct ShellCommand {
5    pub command: String,
6    pub args: Vec<String>,
7}
8
9impl ShellCommand {
10    #[tracing::instrument(skip(self), level = "trace")]
11    pub fn run(&self) -> Result<(), ShellCommandError> {
12        tracing::trace!(command = ?self.command_and_args(), "running shell command");
13        std::process::Command::new(&self.command)
14            .args(&self.args)
15            .spawn()?
16            .wait()?;
17
18        Ok(())
19    }
20
21    pub fn command_and_args(&self) -> Vec<String> {
22        let mut command = vec![self.command.clone()];
23        command.extend(self.args.clone());
24        command
25    }
26}
27
28impl From<ShellCommand> for Vec<ShellCommand> {
29    fn from(process: ShellCommand) -> Self {
30        vec![process]
31    }
32}
33
34impl TryFrom<String> for ShellCommand {
35    type Error = ShellCommandError;
36
37    fn try_from(command: String) -> Result<Self, Self::Error> {
38        let command: &str = command.as_ref();
39        command.try_into()
40    }
41}
42
43impl TryFrom<&str> for ShellCommand {
44    type Error = ShellCommandError;
45
46    fn try_from(command: &str) -> Result<Self, Self::Error> {
47        let command_chunks: Vec<String> = command.split_whitespace().map(String::from).collect();
48
49        if let Some((command, args)) = command_chunks.split_first() {
50            Ok(Self {
51                command: command.clone(),
52                args: args.to_vec(),
53            })
54        } else {
55            Err(ShellCommandError::MalformedError(command.to_string()))
56        }
57    }
58}
59
60#[tracing::instrument(skip(shell_command), level = "trace")]
61pub fn shell<T: Into<String>>(shell_command: T) -> crate::Result<ShellCommand> {
62    let shell_command = shell_command.into();
63
64    tracing::trace!(shell_command = ?shell_command, "converting to shell command");
65
66    Ok(shell_command.try_into()?)
67}