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