1use std::fs::File;
2use std::io::{BufWriter, Write};
3use std::path::Path;
4use std::process::Command;
5
6use anyhow::{anyhow, bail, Context, Result};
7
8use crate::config::Config;
9use crate::model::config::Terraform;
10use crate::model::Document;
11use crate::Bindings;
12
13#[derive(Default)]
14pub struct Builder {
15 config_path: Option<String>,
16}
17
18impl Builder {
19 pub fn config(&mut self, path: impl Into<String>) -> &mut Self {
28 self.config_path = Some(path.into());
29 self
30 }
31
32 pub fn generate(&mut self) -> Result<Bindings> {
34 let config_path = self
35 .config_path
36 .take()
37 .ok_or(anyhow!("missing config path"))?;
38 let cfg = Config::from_file(&config_path)
39 .with_context(|| format!("failed to read config from file {config_path}"))?;
40 let providers = cfg.providers().context("failed to parse providers")?;
41 let version = providers.iter().cloned().collect();
42
43 let terraform_dir = Path::new(&std::env::var("OUT_DIR").unwrap()).join("terraform");
44 std::fs::create_dir_all(&terraform_dir).context("failed to create terraform directory")?;
45 let main_file = terraform_dir.join("main.tf.json");
46
47 let mut config = Terraform::default();
48 for (name, constraint) in &providers {
49 config.add_provider(name, constraint.clone())
50 }
51 let document = Document::from_config(config);
52
53 let file = File::create(main_file).context("failed to write main bindings file")?;
54 let mut writer = BufWriter::new(file);
55 serde_json::to_writer_pretty(&mut writer, &document).unwrap();
56 writer
57 .flush()
58 .context("failed to write to terraform provider document")?;
59
60 let tf_process = Command::new("terraform")
61 .arg(format!("-chdir={}", terraform_dir.to_str().unwrap()))
62 .arg("init")
63 .output()
64 .context("failed to initialize terraform provider")?;
65 if !tf_process.status.success() {
66 print!("{}", String::from_utf8(tf_process.stdout).unwrap());
67 print!("{}", String::from_utf8(tf_process.stderr).unwrap());
68 panic!("failed to initialize Terraform")
69 }
70
71 let tf_process = Command::new("terraform")
72 .arg(format!("-chdir={}", terraform_dir.to_str().unwrap()))
73 .arg("providers")
74 .arg("schema")
75 .arg("-json")
76 .output()
77 .context("failed to read terraform provider schemas")?;
78 if !tf_process.status.success() {
79 print!("{}", String::from_utf8(tf_process.stdout).unwrap());
80 print!("{}", String::from_utf8(tf_process.stderr).unwrap());
81 bail!("failed to read terraform provider schema")
82 }
83 let schema = serde_json::from_slice(&tf_process.stdout[..])
84 .context("failed to parse provider schema")?;
85
86 Ok(Bindings { schema, version })
87 }
88}