terraform_wrapper/commands/
init.rs1use crate::Terraform;
2use crate::command::TerraformCommand;
3use crate::error::Result;
4use crate::exec::{self, CommandOutput};
5
6#[derive(Debug, Clone, Default)]
22pub struct InitCommand {
23 backend: Option<bool>,
24 backend_configs: Vec<(String, String)>,
25 backend_config_files: Vec<String>,
26 from_module: Option<String>,
27 get: Option<bool>,
28 upgrade: bool,
29 reconfigure: bool,
30 migrate_state: bool,
31 plugin_dir: Option<String>,
32 lockfile: Option<String>,
33 lock: Option<bool>,
34 lock_timeout: Option<String>,
35 json: bool,
36 raw_args: Vec<String>,
37}
38
39impl InitCommand {
40 #[must_use]
42 pub fn new() -> Self {
43 Self::default()
44 }
45
46 #[must_use]
48 pub fn backend(mut self, enabled: bool) -> Self {
49 self.backend = Some(enabled);
50 self
51 }
52
53 #[must_use]
55 pub fn backend_config(mut self, key: &str, value: &str) -> Self {
56 self.backend_configs
57 .push((key.to_string(), value.to_string()));
58 self
59 }
60
61 #[must_use]
63 pub fn backend_config_file(mut self, path: &str) -> Self {
64 self.backend_config_files.push(path.to_string());
65 self
66 }
67
68 #[must_use]
70 pub fn from_module(mut self, source: &str) -> Self {
71 self.from_module = Some(source.to_string());
72 self
73 }
74
75 #[must_use]
77 pub fn get(mut self, enabled: bool) -> Self {
78 self.get = Some(enabled);
79 self
80 }
81
82 #[must_use]
84 pub fn upgrade(mut self) -> Self {
85 self.upgrade = true;
86 self
87 }
88
89 #[must_use]
91 pub fn reconfigure(mut self) -> Self {
92 self.reconfigure = true;
93 self
94 }
95
96 #[must_use]
98 pub fn migrate_state(mut self) -> Self {
99 self.migrate_state = true;
100 self
101 }
102
103 #[must_use]
105 pub fn plugin_dir(mut self, path: &str) -> Self {
106 self.plugin_dir = Some(path.to_string());
107 self
108 }
109
110 #[must_use]
112 pub fn lockfile(mut self, mode: &str) -> Self {
113 self.lockfile = Some(mode.to_string());
114 self
115 }
116
117 #[must_use]
119 pub fn lock(mut self, enabled: bool) -> Self {
120 self.lock = Some(enabled);
121 self
122 }
123
124 #[must_use]
126 pub fn lock_timeout(mut self, timeout: &str) -> Self {
127 self.lock_timeout = Some(timeout.to_string());
128 self
129 }
130
131 #[must_use]
133 pub fn json(mut self) -> Self {
134 self.json = true;
135 self
136 }
137
138 #[must_use]
140 pub fn arg(mut self, arg: impl Into<String>) -> Self {
141 self.raw_args.push(arg.into());
142 self
143 }
144}
145
146impl TerraformCommand for InitCommand {
147 type Output = CommandOutput;
148
149 fn args(&self) -> Vec<String> {
150 let mut args = vec!["init".to_string()];
151
152 if let Some(backend) = self.backend {
153 args.push(format!("-backend={backend}"));
154 }
155 for (key, value) in &self.backend_configs {
156 args.push(format!("-backend-config={key}={value}"));
157 }
158 for file in &self.backend_config_files {
159 args.push(format!("-backend-config={file}"));
160 }
161 if let Some(ref source) = self.from_module {
162 args.push(format!("-from-module={source}"));
163 }
164 if let Some(get) = self.get {
165 args.push(format!("-get={get}"));
166 }
167 if self.upgrade {
168 args.push("-upgrade".to_string());
169 }
170 if self.reconfigure {
171 args.push("-reconfigure".to_string());
172 }
173 if self.migrate_state {
174 args.push("-migrate-state".to_string());
175 }
176 if let Some(ref dir) = self.plugin_dir {
177 args.push(format!("-plugin-dir={dir}"));
178 }
179 if let Some(ref mode) = self.lockfile {
180 args.push(format!("-lockfile={mode}"));
181 }
182 if let Some(lock) = self.lock {
183 args.push(format!("-lock={lock}"));
184 }
185 if let Some(ref timeout) = self.lock_timeout {
186 args.push(format!("-lock-timeout={timeout}"));
187 }
188 if self.json {
189 args.push("-json".to_string());
190 }
191 args.extend(self.raw_args.clone());
192 args
193 }
194
195 fn supports_input(&self) -> bool {
196 true
197 }
198
199 async fn execute(&self, tf: &Terraform) -> Result<CommandOutput> {
200 exec::run_terraform(tf, self.prepare_args(tf)).await
201 }
202}
203
204#[cfg(test)]
205mod tests {
206 use super::*;
207
208 #[test]
209 fn default_args() {
210 let cmd = InitCommand::new();
211 assert_eq!(cmd.args(), vec!["init"]);
212 }
213
214 #[test]
215 fn all_options() {
216 let cmd = InitCommand::new()
217 .backend(false)
218 .backend_config("key", "value")
219 .backend_config_file("backend.hcl")
220 .from_module("./staging")
221 .get(false)
222 .upgrade()
223 .reconfigure()
224 .plugin_dir("/plugins")
225 .lockfile("readonly")
226 .lock(false)
227 .lock_timeout("10s")
228 .json();
229 let args = cmd.args();
230 assert!(args.contains(&"-backend=false".to_string()));
231 assert!(args.contains(&"-backend-config=key=value".to_string()));
232 assert!(args.contains(&"-backend-config=backend.hcl".to_string()));
233 assert!(args.contains(&"-from-module=./staging".to_string()));
234 assert!(args.contains(&"-get=false".to_string()));
235 assert!(args.contains(&"-upgrade".to_string()));
236 assert!(args.contains(&"-reconfigure".to_string()));
237 assert!(args.contains(&"-plugin-dir=/plugins".to_string()));
238 assert!(args.contains(&"-lockfile=readonly".to_string()));
239 assert!(args.contains(&"-lock=false".to_string()));
240 assert!(args.contains(&"-lock-timeout=10s".to_string()));
241 assert!(args.contains(&"-json".to_string()));
242 }
243
244 #[test]
245 fn backend_disabled() {
246 let cmd = InitCommand::new().backend(false);
247 assert!(cmd.args().contains(&"-backend=false".to_string()));
248 }
249
250 #[test]
251 fn from_module_source() {
252 let cmd = InitCommand::new().from_module("./staging");
253 assert!(cmd.args().contains(&"-from-module=./staging".to_string()));
254 }
255
256 #[test]
257 fn get_disabled() {
258 let cmd = InitCommand::new().get(false);
259 assert!(cmd.args().contains(&"-get=false".to_string()));
260 }
261
262 #[test]
263 fn lockfile_readonly() {
264 let cmd = InitCommand::new().lockfile("readonly");
265 assert!(cmd.args().contains(&"-lockfile=readonly".to_string()));
266 }
267
268 #[test]
269 fn json_output() {
270 let cmd = InitCommand::new().json();
271 assert!(cmd.args().contains(&"-json".to_string()));
272 }
273
274 #[test]
275 fn raw_arg_escape_hatch() {
276 let cmd = InitCommand::new().arg("-no-color");
277 let args = cmd.args();
278 assert!(args.contains(&"-no-color".to_string()));
279 }
280}