terraform_wrapper/commands/
output.rs1use std::collections::HashMap;
2
3use crate::Terraform;
4use crate::command::TerraformCommand;
5use crate::error::Result;
6use crate::exec;
7
8#[derive(Debug, Clone)]
15pub enum OutputResult {
16 Raw(String),
18 #[cfg(feature = "json")]
20 Json(HashMap<String, crate::types::output::OutputValue>),
21 #[cfg(feature = "json")]
23 Single(crate::types::output::OutputValue),
24 Plain(exec::CommandOutput),
26}
27
28#[derive(Debug, Clone, Default)]
50pub struct OutputCommand {
51 name: Option<String>,
52 json: bool,
53 raw: bool,
54 raw_args: Vec<String>,
55}
56
57impl OutputCommand {
58 #[must_use]
60 pub fn new() -> Self {
61 Self::default()
62 }
63
64 #[must_use]
66 pub fn name(mut self, name: &str) -> Self {
67 self.name = Some(name.to_string());
68 self
69 }
70
71 #[must_use]
73 pub fn json(mut self) -> Self {
74 self.json = true;
75 self
76 }
77
78 #[must_use]
80 pub fn raw(mut self) -> Self {
81 self.raw = true;
82 self
83 }
84
85 #[must_use]
87 pub fn arg(mut self, arg: impl Into<String>) -> Self {
88 self.raw_args.push(arg.into());
89 self
90 }
91}
92
93impl TerraformCommand for OutputCommand {
94 type Output = OutputResult;
95
96 fn args(&self) -> Vec<String> {
97 let mut args = vec!["output".to_string()];
98 if self.json {
99 args.push("-json".to_string());
100 }
101 if self.raw {
102 args.push("-raw".to_string());
103 }
104 args.extend(self.raw_args.clone());
105 if let Some(ref name) = self.name {
106 args.push(name.clone());
107 }
108 args
109 }
110
111 async fn execute(&self, tf: &Terraform) -> Result<OutputResult> {
112 let output = exec::run_terraform(tf, self.args()).await?;
113
114 if self.raw {
115 return Ok(OutputResult::Raw(output.stdout.trim_end().to_string()));
116 }
117
118 #[cfg(feature = "json")]
119 if self.json {
120 if self.name.is_some() {
121 let value: crate::types::output::OutputValue = serde_json::from_str(&output.stdout)
122 .map_err(|e| crate::error::Error::ParseError {
123 message: format!("failed to parse output json: {e}"),
124 })?;
125 return Ok(OutputResult::Single(value));
126 }
127 let values: HashMap<String, crate::types::output::OutputValue> =
128 serde_json::from_str(&output.stdout).map_err(|e| {
129 crate::error::Error::ParseError {
130 message: format!("failed to parse output json: {e}"),
131 }
132 })?;
133 return Ok(OutputResult::Json(values));
134 }
135
136 Ok(OutputResult::Plain(output))
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143
144 #[test]
145 fn default_args() {
146 let cmd = OutputCommand::new();
147 assert_eq!(cmd.args(), vec!["output"]);
148 }
149
150 #[test]
151 fn json_all_outputs() {
152 let cmd = OutputCommand::new().json();
153 assert_eq!(cmd.args(), vec!["output", "-json"]);
154 }
155
156 #[test]
157 fn raw_named_output() {
158 let cmd = OutputCommand::new().name("public_ip").raw();
159 let args = cmd.args();
160 assert_eq!(args, vec!["output", "-raw", "public_ip"]);
161 }
162
163 #[test]
164 fn json_named_output() {
165 let cmd = OutputCommand::new().name("vpc_id").json();
166 let args = cmd.args();
167 assert_eq!(args, vec!["output", "-json", "vpc_id"]);
168 }
169
170 #[test]
171 fn name_at_end() {
172 let cmd = OutputCommand::new().name("endpoint").arg("-no-color");
173 let args = cmd.args();
174 assert_eq!(args.last().unwrap(), "endpoint");
176 }
177}