dotfiles_actions/exec/
action.rs1use std::path::Path;
25
26use dotfiles_core::action::SKIP_IN_CI_SETTING;
27use dotfiles_core::error::execution_error;
28use dotfiles_core::error::DotfilesError;
29use dotfiles_core::settings::{initialize_settings_object, Setting, Settings};
30use dotfiles_core::{yaml_util, Action};
31use strict_yaml_rust::StrictYaml;
32use subprocess::Exec;
33
34pub const ECHO_SETTING: &str = "echo";
36pub const COMMAND_SETTING: &str = "cmd";
38pub const DESCRIPTION_SETTING: &str = "description";
40
41pub fn default_settings() -> Settings {
43 initialize_settings_object(&[
44 (ECHO_SETTING.to_owned(), Setting::Boolean(false)),
45 (SKIP_IN_CI_SETTING.to_owned(), Setting::Boolean(false)),
46 ])
47}
48
49#[derive(Eq, PartialEq, Debug)]
51pub struct ExecAction {
52 skip_in_ci: bool,
54 command: String,
56 description: Option<String>,
58 echo: bool,
60}
61
62impl ExecAction {
63 pub fn new(
65 skip_in_ci: bool,
66 command: String,
67 description: Option<String>,
68 echo: bool,
69 current_dir: &Path,
70 ) -> Result<Self, DotfilesError> {
71 let action = ExecAction {
72 skip_in_ci,
73 command: format!(
74 "cd \"{}\" && {}",
75 current_dir.as_os_str().to_str().unwrap(),
76 command
77 ),
78 description,
79 echo,
80 };
81 log::trace!("Creating new {:?}", action);
82 Ok(action)
83 }
84 pub fn command(&self) -> &str {
86 self.command.as_str()
87 }
88
89 pub fn echo(&self) -> bool {
91 self.echo
92 }
93
94 pub fn description(&self) -> Option<&String> {
96 self.description.as_ref()
97 }
98}
99
100impl Action for ExecAction {
101 fn execute(&self) -> Result<(), DotfilesError> {
102 if let Some(description) = self.description.as_ref() {
103 log::info!("{}", description);
104 }
105 if self.echo {
106 log::info!("Running command: {0}", self.command);
107 }
108 Exec::shell(self.command()).join().map_or_else(
109 |err| {
110 Err(DotfilesError::from(
111 format!(
112 "Couldn't run command `{0}`, failed with error {1}",
113 self.command(),
114 err
115 ),
116 execution_error(Some(err), None),
117 ))
118 },
119 |status| match status.success() {
120 true => Ok(()),
121 false => Err(DotfilesError::from(
122 format!(
123 "Command `{0}` failed with error code {1}",
124 self.command(),
125 status.code().unwrap()
126 ),
127 execution_error(None, Some(status)),
128 )),
129 },
130 )
131 }
132
133 fn skip_in_ci(&self) -> bool {
134 self.skip_in_ci
135 }
136}
137
138pub fn parse_action(
140 settings: &Settings,
141 yaml: &StrictYaml,
142 current_dir: &Path,
143) -> Result<ExecAction, DotfilesError> {
144 let defaults = default_settings();
145 ExecAction::new(
146 yaml_util::get_boolean_setting_from_yaml_or_context(
147 SKIP_IN_CI_SETTING,
148 yaml,
149 settings,
150 &defaults,
151 )?,
152 yaml_util::get_string_content_or_keyed_value(yaml, Some(COMMAND_SETTING))?,
153 yaml_util::get_string_setting_from_yaml_or_context(
154 DESCRIPTION_SETTING,
155 yaml,
156 settings,
157 &defaults,
158 )
159 .ok(),
160 yaml_util::get_boolean_setting_from_yaml_or_context(ECHO_SETTING, yaml, settings, &defaults)?,
161 current_dir,
162 )
163}