Skip to main content

terraform_wrapper/commands/
raw.rs

1use crate::Terraform;
2use crate::command::TerraformCommand;
3use crate::error::Result;
4use crate::exec::{self, CommandOutput};
5
6/// Escape-hatch command for running any Terraform subcommand.
7///
8/// Use this when you need a subcommand that doesn't have a dedicated type,
9/// while still benefiting from the [`Terraform`] client's binary resolution,
10/// working directory, environment variables, and global arguments.
11///
12/// ```no_run
13/// # async fn example() -> terraform_wrapper::error::Result<()> {
14/// use terraform_wrapper::{Terraform, TerraformCommand};
15/// use terraform_wrapper::commands::raw::RawCommand;
16///
17/// let tf = Terraform::builder().working_dir("/tmp/infra").build()?;
18///
19/// // Run any subcommand with arbitrary flags
20/// let output = RawCommand::new("console")
21///     .arg("-var=region=us-west-2")
22///     .execute(&tf)
23///     .await?;
24/// # Ok(())
25/// # }
26/// ```
27#[derive(Debug, Clone)]
28pub struct RawCommand {
29    subcommand: String,
30    extra_args: Vec<String>,
31}
32
33impl RawCommand {
34    /// Create a new raw command for the given subcommand.
35    #[must_use]
36    pub fn new(subcommand: &str) -> Self {
37        Self {
38            subcommand: subcommand.to_string(),
39            extra_args: Vec::new(),
40        }
41    }
42
43    /// Add an argument to the command.
44    #[must_use]
45    pub fn arg(mut self, arg: impl Into<String>) -> Self {
46        self.extra_args.push(arg.into());
47        self
48    }
49
50    /// Add multiple arguments to the command.
51    #[must_use]
52    pub fn with_args(mut self, args: impl IntoIterator<Item = impl Into<String>>) -> Self {
53        self.extra_args.extend(args.into_iter().map(Into::into));
54        self
55    }
56}
57
58impl TerraformCommand for RawCommand {
59    type Output = CommandOutput;
60
61    fn args(&self) -> Vec<String> {
62        let mut args = vec![self.subcommand.clone()];
63        args.extend(self.extra_args.clone());
64        args
65    }
66
67    async fn execute(&self, tf: &Terraform) -> Result<CommandOutput> {
68        exec::run_terraform(tf, self.args()).await
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75
76    #[test]
77    fn subcommand_only() {
78        let cmd = RawCommand::new("console");
79        assert_eq!(cmd.args(), vec!["console"]);
80    }
81
82    #[test]
83    fn with_single_arg() {
84        let cmd = RawCommand::new("taint").arg("aws_instance.example");
85        assert_eq!(cmd.args(), vec!["taint", "aws_instance.example"]);
86    }
87
88    #[test]
89    fn with_multiple_args() {
90        let cmd = RawCommand::new("untaint").with_args(["-lock=false", "aws_instance.example"]);
91        assert_eq!(
92            cmd.args(),
93            vec!["untaint", "-lock=false", "aws_instance.example"]
94        );
95    }
96}