dotfiles_actions/exec/
action.rs1use std::marker::PhantomData;
25use std::path::Path;
26
27use dotfiles_core::error::execution_error;
28use dotfiles_core::error::DotfilesError;
29use dotfiles_core_macros::ConditionalAction;
30use subprocess::Exec;
31use subprocess::ExitStatus;
32
33use dotfiles_core::Action;
34
35#[derive(Eq, PartialEq, Debug, ConditionalAction)]
37pub struct ExecAction<'a> {
38  skip_in_ci: bool,
40  command: String,
42  description: Option<String>,
44  echo: bool,
46  phantom_data: PhantomData<&'a String>,
47}
48
49impl<'a> ExecAction<'a> {
50  pub fn new(
52    skip_in_ci: bool,
53    command: String,
54    description: Option<String>,
55    echo: bool,
56    current_dir: &Path,
57  ) -> Result<Self, DotfilesError> {
58    let action = ExecAction {
59      skip_in_ci,
60      command: format!(
61        "cd \"{}\" && {}",
62        current_dir.as_os_str().to_str().unwrap(),
63        command
64      ),
65      description,
66      echo,
67      phantom_data: PhantomData,
68    };
69    log::trace!("Creating new {:?}", action);
70    Ok(action)
71  }
72  pub fn command(&self) -> &str {
74    self.command.as_str()
75  }
76
77  pub fn echo(&self) -> bool {
79    self.echo
80  }
81
82  pub fn description(&self) -> Option<&String> {
84    self.description.as_ref()
85  }
86}
87
88impl<'a> Action<'a> for ExecAction<'a> {
89  fn execute(&self) -> Result<(), DotfilesError> {
90    if let Some(description) = self.description.as_ref() {
91      log::info!("{}", description);
92    }
93    if self.echo {
94      log::info!("Running command: {0}", self.command);
95    }
96    Exec::shell(self.command()).join().map_or_else(
97      |err| {
98        Err(DotfilesError::from(
99          format!(
100            "Couldn't run command `{0}`, failed with error {1}",
101            self.command(),
102            err
103          ),
104          execution_error(Some(err), None),
105        ))
106      },
107      |status| match status {
108        ExitStatus::Exited(0) => Ok(()),
109        ExitStatus::Exited(code) => Err(DotfilesError::from(
110          format!(
111            "Command `{0}` failed with error code {1}",
112            self.command(),
113            code
114          ),
115          execution_error(None, Some(status)),
116        )),
117        _ => Err(DotfilesError::from(
118          format!(
119            "Unexpected error while running command `{0}`",
120            self.command()
121          ),
122          execution_error(None, Some(status)),
123        )),
124      },
125    )
126  }
127}