tf_bindgen/
builder.rs

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    /// Set path of configuration file to use.
20    ///
21    /// # Config
22    ///
23    /// ```toml
24    /// [terraform]
25    /// kubernetes = "2.17.0"
26    /// ```
27    pub fn config(&mut self, path: impl Into<String>) -> &mut Self {
28        self.config_path = Some(path.into());
29        self
30    }
31
32    /// Read configuration file and generate rust files from terraform providers.
33    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}