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 self.work_dir.is_some() {
59 cmd.current_dir(self.work_dir.as_ref().with_context(|| "Failed to get 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
91#[cfg(test)]
92mod test {
93 use super::*;
94
95 #[test]
96 fn test_precondition_1() -> anyhow::Result<()> {
97 {
98 let yaml = "
99 command: 'echo \"Hello, World!\"'
100 message: 'This is a message'
101 ";
102 let precondition = serde_yaml::from_str::<Precondition>(yaml)?;
103
104 assert_eq!(precondition.command, "echo \"Hello, World!\"");
105 assert_eq!(precondition.message, Some("This is a message".into()));
106 assert_eq!(precondition.work_dir, None);
107 assert_eq!(precondition.verbose, None);
108
109 Ok(())
110 }
111 }
112
113 #[test]
114 fn test_precondition_2() -> anyhow::Result<()> {
115 {
116 let yaml = "
117 command: 'echo \"Hello, World!\"'
118 ";
119 let precondition = serde_yaml::from_str::<Precondition>(yaml)?;
120
121 assert_eq!(precondition.command, "echo \"Hello, World!\"");
122 assert_eq!(precondition.message, None);
123 assert_eq!(precondition.work_dir, None);
124 assert_eq!(precondition.verbose, None);
125
126 Ok(())
127 }
128 }
129
130 #[test]
131 fn test_precondition_3() -> anyhow::Result<()> {
132 {
133 let yaml = "
134 command: 'echo \"Hello, World!\"'
135 message: null
136 ";
137 let precondition = serde_yaml::from_str::<Precondition>(yaml)?;
138
139 assert_eq!(precondition.command, "echo \"Hello, World!\"");
140 assert_eq!(precondition.message, None);
141 assert_eq!(precondition.work_dir, None);
142 assert_eq!(precondition.verbose, None);
143
144 Ok(())
145 }
146 }
147
148 #[test]
149 fn test_precondition_4() -> anyhow::Result<()> {
150 {
151 let yaml = "
152 command: 'echo \"Hello, World!\"'
153 work_dir: /tmp
154 ";
155 let precondition = serde_yaml::from_str::<Precondition>(yaml)?;
156
157 assert_eq!(precondition.command, "echo \"Hello, World!\"");
158 assert_eq!(precondition.message, None);
159 assert_eq!(precondition.work_dir, Some("/tmp".into()));
160 assert_eq!(precondition.verbose, None);
161
162 Ok(())
163 }
164 }
165
166 #[test]
167 fn test_precondition_5() -> anyhow::Result<()> {
168 {
169 let yaml = "
170 command: 'echo \"Hello, World!\"'
171 verbose: true
172 ";
173 let precondition = serde_yaml::from_str::<Precondition>(yaml)?;
174
175 assert_eq!(precondition.command, "echo \"Hello, World!\"");
176 assert_eq!(precondition.message, None);
177 assert_eq!(precondition.work_dir, None);
178 assert_eq!(precondition.verbose, Some(true));
179
180 Ok(())
181 }
182 }
183
184 #[test]
185 fn test_precondition_6() -> anyhow::Result<()> {
186 {
187 let yaml = "
188 command: ls -la
189 message: Listing directory contents
190 shell: bash
191 work_dir: /tmp
192 verbose: false
193 ";
194 let precondition = serde_yaml::from_str::<Precondition>(yaml)?;
195
196 assert_eq!(precondition.command, "ls -la");
197 assert_eq!(
198 precondition.message,
199 Some("Listing directory contents".to_string())
200 );
201 if let Some(shell) = precondition.shell {
202 assert_eq!(shell.cmd(), "bash".to_string());
203 } else {
204 panic!("Expected shell to be Some");
205 }
206 assert_eq!(precondition.work_dir, Some("/tmp".into()));
207 assert_eq!(precondition.verbose, Some(false));
208
209 Ok(())
210 }
211 }
212}