Skip to main content

terraform_wrapper/commands/
import.rs

1use crate::Terraform;
2use crate::command::TerraformCommand;
3use crate::error::Result;
4use crate::exec::{self, CommandOutput};
5
6/// Command for importing existing infrastructure into Terraform state.
7///
8/// Associates an existing resource with a Terraform resource address.
9///
10/// ```no_run
11/// # async fn example() -> terraform_wrapper::error::Result<()> {
12/// use terraform_wrapper::{Terraform, TerraformCommand};
13/// use terraform_wrapper::commands::import::ImportCommand;
14///
15/// let tf = Terraform::builder().working_dir("/tmp/infra").build()?;
16/// ImportCommand::new("aws_instance.web", "i-1234567890abcdef0")
17///     .execute(&tf)
18///     .await?;
19/// # Ok(())
20/// # }
21/// ```
22#[derive(Debug, Clone)]
23pub struct ImportCommand {
24    address: String,
25    id: String,
26    vars: Vec<(String, String)>,
27    var_files: Vec<String>,
28    lock: Option<bool>,
29    lock_timeout: Option<String>,
30    raw_args: Vec<String>,
31}
32
33impl ImportCommand {
34    /// Create a new import command.
35    ///
36    /// - `address`: The Terraform resource address (e.g., "aws_instance.web")
37    /// - `id`: The provider-specific resource ID (e.g., "i-1234567890abcdef0")
38    #[must_use]
39    pub fn new(address: &str, id: &str) -> Self {
40        Self {
41            address: address.to_string(),
42            id: id.to_string(),
43            vars: Vec::new(),
44            var_files: Vec::new(),
45            lock: None,
46            lock_timeout: None,
47            raw_args: Vec::new(),
48        }
49    }
50
51    /// Set a variable value (`-var="name=value"`).
52    #[must_use]
53    pub fn var(mut self, name: &str, value: &str) -> Self {
54        self.vars.push((name.to_string(), value.to_string()));
55        self
56    }
57
58    /// Add a variable definitions file (`-var-file`).
59    #[must_use]
60    pub fn var_file(mut self, path: &str) -> Self {
61        self.var_files.push(path.to_string());
62        self
63    }
64
65    /// Enable or disable state locking (`-lock`).
66    #[must_use]
67    pub fn lock(mut self, enabled: bool) -> Self {
68        self.lock = Some(enabled);
69        self
70    }
71
72    /// Duration to wait for state lock (`-lock-timeout`).
73    #[must_use]
74    pub fn lock_timeout(mut self, timeout: &str) -> Self {
75        self.lock_timeout = Some(timeout.to_string());
76        self
77    }
78
79    /// Add a raw argument (escape hatch for unsupported options).
80    #[must_use]
81    pub fn arg(mut self, arg: impl Into<String>) -> Self {
82        self.raw_args.push(arg.into());
83        self
84    }
85}
86
87impl TerraformCommand for ImportCommand {
88    type Output = CommandOutput;
89
90    fn args(&self) -> Vec<String> {
91        let mut args = vec!["import".to_string()];
92        for (name, value) in &self.vars {
93            args.push(format!("-var={name}={value}"));
94        }
95        for file in &self.var_files {
96            args.push(format!("-var-file={file}"));
97        }
98        if let Some(lock) = self.lock {
99            args.push(format!("-lock={lock}"));
100        }
101        if let Some(ref timeout) = self.lock_timeout {
102            args.push(format!("-lock-timeout={timeout}"));
103        }
104        args.extend(self.raw_args.clone());
105        // Positional args at end: address, then id
106        args.push(self.address.clone());
107        args.push(self.id.clone());
108        args
109    }
110
111    fn supports_input(&self) -> bool {
112        true
113    }
114
115    async fn execute(&self, tf: &Terraform) -> Result<CommandOutput> {
116        exec::run_terraform(tf, self.prepare_args(tf)).await
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123
124    #[test]
125    fn basic_args() {
126        let cmd = ImportCommand::new("aws_instance.web", "i-123");
127        assert_eq!(cmd.args(), vec!["import", "aws_instance.web", "i-123"]);
128    }
129
130    #[test]
131    fn with_vars() {
132        let cmd = ImportCommand::new("aws_instance.web", "i-123").var("region", "us-west-2");
133        let args = cmd.args();
134        assert!(args.contains(&"-var=region=us-west-2".to_string()));
135        // Positional args still at end
136        let len = args.len();
137        assert_eq!(args[len - 2], "aws_instance.web");
138        assert_eq!(args[len - 1], "i-123");
139    }
140
141    #[test]
142    fn with_lock_options() {
143        let cmd = ImportCommand::new("aws_instance.web", "i-123")
144            .lock(false)
145            .lock_timeout("10s");
146        let args = cmd.args();
147        assert!(args.contains(&"-lock=false".to_string()));
148        assert!(args.contains(&"-lock-timeout=10s".to_string()));
149    }
150}