Skip to main content

terraform_wrapper/commands/
workspace.rs

1use crate::Terraform;
2use crate::command::TerraformCommand;
3use crate::error::Result;
4use crate::exec::{self, CommandOutput};
5
6/// The workspace subcommand to execute.
7#[derive(Debug, Clone)]
8pub enum WorkspaceSubcommand {
9    /// List all workspaces.
10    List,
11    /// Show the current workspace name.
12    Show,
13    /// Create a new workspace.
14    New(String),
15    /// Switch to an existing workspace.
16    Select(String),
17    /// Delete a workspace.
18    Delete(String),
19}
20
21/// Command for managing Terraform workspaces.
22///
23/// ```no_run
24/// # async fn example() -> terraform_wrapper::error::Result<()> {
25/// use terraform_wrapper::{Terraform, TerraformCommand};
26/// use terraform_wrapper::commands::workspace::WorkspaceCommand;
27///
28/// let tf = Terraform::builder().working_dir("/tmp/infra").build()?;
29///
30/// // List workspaces
31/// let output = WorkspaceCommand::list().execute(&tf).await?;
32///
33/// // Create and switch to a new workspace
34/// WorkspaceCommand::new_workspace("staging").execute(&tf).await?;
35///
36/// // Switch back
37/// WorkspaceCommand::select("default").execute(&tf).await?;
38///
39/// // Delete
40/// WorkspaceCommand::delete("staging").execute(&tf).await?;
41/// # Ok(())
42/// # }
43/// ```
44#[derive(Debug, Clone)]
45pub struct WorkspaceCommand {
46    subcommand: WorkspaceSubcommand,
47    force: bool,
48    raw_args: Vec<String>,
49}
50
51impl WorkspaceCommand {
52    /// List all workspaces.
53    #[must_use]
54    pub fn list() -> Self {
55        Self {
56            subcommand: WorkspaceSubcommand::List,
57            force: false,
58            raw_args: Vec::new(),
59        }
60    }
61
62    /// Show the current workspace name.
63    #[must_use]
64    pub fn show() -> Self {
65        Self {
66            subcommand: WorkspaceSubcommand::Show,
67            force: false,
68            raw_args: Vec::new(),
69        }
70    }
71
72    /// Create a new workspace.
73    #[must_use]
74    pub fn new_workspace(name: &str) -> Self {
75        Self {
76            subcommand: WorkspaceSubcommand::New(name.to_string()),
77            force: false,
78            raw_args: Vec::new(),
79        }
80    }
81
82    /// Select (switch to) an existing workspace.
83    #[must_use]
84    pub fn select(name: &str) -> Self {
85        Self {
86            subcommand: WorkspaceSubcommand::Select(name.to_string()),
87            force: false,
88            raw_args: Vec::new(),
89        }
90    }
91
92    /// Delete a workspace.
93    #[must_use]
94    pub fn delete(name: &str) -> Self {
95        Self {
96            subcommand: WorkspaceSubcommand::Delete(name.to_string()),
97            force: false,
98            raw_args: Vec::new(),
99        }
100    }
101
102    /// Force deletion of a non-empty workspace (`-force`).
103    #[must_use]
104    pub fn force(mut self) -> Self {
105        self.force = true;
106        self
107    }
108
109    /// Add a raw argument (escape hatch for unsupported options).
110    #[must_use]
111    pub fn arg(mut self, arg: impl Into<String>) -> Self {
112        self.raw_args.push(arg.into());
113        self
114    }
115}
116
117impl TerraformCommand for WorkspaceCommand {
118    type Output = CommandOutput;
119
120    fn args(&self) -> Vec<String> {
121        let mut args = vec!["workspace".to_string()];
122        match &self.subcommand {
123            WorkspaceSubcommand::List => args.push("list".to_string()),
124            WorkspaceSubcommand::Show => args.push("show".to_string()),
125            WorkspaceSubcommand::New(name) => {
126                args.push("new".to_string());
127                args.push(name.clone());
128            }
129            WorkspaceSubcommand::Select(name) => {
130                args.push("select".to_string());
131                args.push(name.clone());
132            }
133            WorkspaceSubcommand::Delete(name) => {
134                args.push("delete".to_string());
135                if self.force {
136                    args.push("-force".to_string());
137                }
138                args.push(name.clone());
139            }
140        }
141        args.extend(self.raw_args.clone());
142        args
143    }
144
145    async fn execute(&self, tf: &Terraform) -> Result<CommandOutput> {
146        exec::run_terraform(tf, self.args()).await
147    }
148}
149
150#[cfg(test)]
151mod tests {
152    use super::*;
153
154    #[test]
155    fn list_args() {
156        let cmd = WorkspaceCommand::list();
157        assert_eq!(cmd.args(), vec!["workspace", "list"]);
158    }
159
160    #[test]
161    fn show_args() {
162        let cmd = WorkspaceCommand::show();
163        assert_eq!(cmd.args(), vec!["workspace", "show"]);
164    }
165
166    #[test]
167    fn new_args() {
168        let cmd = WorkspaceCommand::new_workspace("staging");
169        assert_eq!(cmd.args(), vec!["workspace", "new", "staging"]);
170    }
171
172    #[test]
173    fn select_args() {
174        let cmd = WorkspaceCommand::select("production");
175        assert_eq!(cmd.args(), vec!["workspace", "select", "production"]);
176    }
177
178    #[test]
179    fn delete_args() {
180        let cmd = WorkspaceCommand::delete("staging");
181        assert_eq!(cmd.args(), vec!["workspace", "delete", "staging"]);
182    }
183
184    #[test]
185    fn delete_force_args() {
186        let cmd = WorkspaceCommand::delete("staging").force();
187        assert_eq!(cmd.args(), vec!["workspace", "delete", "-force", "staging"]);
188    }
189}