mk_lib/schema/
precondition.rs1use anyhow::Context as _;
2use serde::Deserialize;
3use std::io::{
4 BufRead as _,
5 BufReader,
6};
7use std::thread;
8
9use super::{
10 Shell,
11 TaskContext,
12};
13use crate::defaults::default_verbose;
14use crate::handle_output;
15use crate::schema::get_output_handler;
16
17#[derive(Debug, Default, Deserialize)]
20pub struct Precondition {
21 pub command: String,
23
24 #[serde(default)]
26 pub message: Option<String>,
27
28 #[serde(default)]
30 pub shell: Option<Shell>,
31
32 #[serde(default)]
34 pub work_dir: Option<String>,
35
36 #[serde(default)]
38 pub verbose: Option<bool>,
39}
40
41impl Precondition {
42 pub fn execute(&self, context: &TaskContext) -> anyhow::Result<()> {
43 assert!(!self.command.is_empty());
44
45 let verbose = self.verbose(context);
46
47 let stdout = get_output_handler(verbose);
48 let stderr = get_output_handler(verbose);
49
50 let mut cmd = self
51 .shell
52 .as_ref()
53 .map(|shell| shell.proc())
54 .unwrap_or_else(|| context.shell().proc());
55
56 cmd.arg(self.command.clone()).stdout(stdout).stderr(stderr);
57
58 if let Some(work_dir) = self.resolved_work_dir(context) {
59 cmd.current_dir(work_dir);
60 }
61
62 for (key, value) in context.env_vars.iter() {
64 cmd.env(key, value);
65 }
66
67 let mut cmd = cmd.spawn()?;
68
69 if verbose {
70 handle_output!(cmd.stdout, context);
71 handle_output!(cmd.stderr, context);
72 }
73
74 let status = cmd.wait()?;
75 if !status.success() {
76 if let Some(message) = &self.message {
77 anyhow::bail!("Precondition failed - {}", message);
78 } else {
79 anyhow::bail!("Precondition failed - {}", self.command);
80 }
81 }
82
83 Ok(())
84 }
85
86 fn verbose(&self, context: &TaskContext) -> bool {
87 self.verbose.or(context.verbose).unwrap_or(default_verbose())
88 }
89
90 fn resolved_work_dir(&self, context: &TaskContext) -> Option<std::path::PathBuf> {
91 self
92 .work_dir
93 .as_ref()
94 .map(|work_dir| context.resolve_from_config(work_dir))
95 }
96}
97
98#[cfg(test)]
99mod test {
100 use super::*;
101
102 #[test]
103 fn test_precondition_1() -> anyhow::Result<()> {
104 {
105 let yaml = "
106 command: 'echo \"Hello, World!\"'
107 message: 'This is a message'
108 ";
109 let precondition = serde_yaml::from_str::<Precondition>(yaml)?;
110
111 assert_eq!(precondition.command, "echo \"Hello, World!\"");
112 assert_eq!(precondition.message, Some("This is a message".into()));
113 assert_eq!(precondition.work_dir, None);
114 assert_eq!(precondition.verbose, None);
115
116 Ok(())
117 }
118 }
119
120 #[test]
121 fn test_precondition_2() -> anyhow::Result<()> {
122 {
123 let yaml = "
124 command: 'echo \"Hello, World!\"'
125 ";
126 let precondition = serde_yaml::from_str::<Precondition>(yaml)?;
127
128 assert_eq!(precondition.command, "echo \"Hello, World!\"");
129 assert_eq!(precondition.message, None);
130 assert_eq!(precondition.work_dir, None);
131 assert_eq!(precondition.verbose, None);
132
133 Ok(())
134 }
135 }
136
137 #[test]
138 fn test_precondition_3() -> anyhow::Result<()> {
139 {
140 let yaml = "
141 command: 'echo \"Hello, World!\"'
142 message: null
143 ";
144 let precondition = serde_yaml::from_str::<Precondition>(yaml)?;
145
146 assert_eq!(precondition.command, "echo \"Hello, World!\"");
147 assert_eq!(precondition.message, None);
148 assert_eq!(precondition.work_dir, None);
149 assert_eq!(precondition.verbose, None);
150
151 Ok(())
152 }
153 }
154
155 #[test]
156 fn test_precondition_4() -> anyhow::Result<()> {
157 {
158 let yaml = "
159 command: 'echo \"Hello, World!\"'
160 work_dir: /tmp
161 ";
162 let precondition = serde_yaml::from_str::<Precondition>(yaml)?;
163
164 assert_eq!(precondition.command, "echo \"Hello, World!\"");
165 assert_eq!(precondition.message, None);
166 assert_eq!(precondition.work_dir, Some("/tmp".into()));
167 assert_eq!(precondition.verbose, None);
168
169 Ok(())
170 }
171 }
172
173 #[test]
174 fn test_precondition_5() -> anyhow::Result<()> {
175 {
176 let yaml = "
177 command: 'echo \"Hello, World!\"'
178 verbose: true
179 ";
180 let precondition = serde_yaml::from_str::<Precondition>(yaml)?;
181
182 assert_eq!(precondition.command, "echo \"Hello, World!\"");
183 assert_eq!(precondition.message, None);
184 assert_eq!(precondition.work_dir, None);
185 assert_eq!(precondition.verbose, Some(true));
186
187 Ok(())
188 }
189 }
190
191 #[test]
192 fn test_precondition_6() -> anyhow::Result<()> {
193 {
194 let yaml = "
195 command: ls -la
196 message: Listing directory contents
197 shell: bash
198 work_dir: /tmp
199 verbose: false
200 ";
201 let precondition = serde_yaml::from_str::<Precondition>(yaml)?;
202
203 assert_eq!(precondition.command, "ls -la");
204 assert_eq!(
205 precondition.message,
206 Some("Listing directory contents".to_string())
207 );
208 if let Some(shell) = precondition.shell {
209 assert_eq!(shell.cmd(), "bash".to_string());
210 } else {
211 panic!("Expected shell to be Some");
212 }
213 assert_eq!(precondition.work_dir, Some("/tmp".into()));
214 assert_eq!(precondition.verbose, Some(false));
215
216 Ok(())
217 }
218 }
219}