pulumi_wasm_build/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
//! `pulumi_wasm_build` generates glue code for given Pulumi provider
//! ## Example
//! Select Pulumi provider you want to use. For demonstration purposes I'll choose [Random](https://www.pulumi.com/registry/packages/random/)
//!
//! Here we will have the provider glue and our code in single library. Due to compilation times
//! it is recommended to separate glue code and custom code.
//! ```bash
//! $ cargo new --lib random && cd random
//! ```
//!
//! First, add `pulumi_wasm_build`, `bon`, `serde`, `anyhow` and `wit-bindgen` to `Cargo.toml`:
//!
//! ```bash
//! $ cargo add --build pulumi_wasm_build
//! $ cargo add bon
//! $ cargo add serde --features derive
//! $ cargo add anyhow
//! $ cargo add wit-bindgen
//! ```
//!
//! To generate glue code, use `pulumi_wasm_build` in `build.rs`
//! ```rust,no_run
//! use std::error::Error;
//! fn main() -> Result<(), Box<dyn Error>> {
//!     pulumi_wasm_build::generate("random", "4.15.0")?;
//!     Ok(())
//! }
//! ```
//!
//! Then add generated code to `lib.rs` and use it in `#[pulumi_main]`
//! ```rust,ignore
//! mod random {
//!   include_provider!("random");
//! }
//!
//! use random::random_string::RandomStringArgs;
//! use random::random_string;
//! use pulumi_wasm_rust::pulumi_main;
//! use anyhow::Result;
//!
//! #[pulumi_main]
//! fn main() -> Result<()> {
//!   let random_string = random_string::create(
//!     "test",
//!     RandomStringArgs::builder().length(32).build_struct()
//!   );
//!   Ok(())
//! }
//! ```

use anyhow::{Context, Result};
use pulumi_wasm_generator::{extract_micro_package, generate_combined};
use std::path::Path;
use std::{env, fs};

/// Generates glue code for given provider, version and modules. Can be included using [`pulumi_wasm_rust::include_provider!(provider_name)`]
/// Modules for provider can be found in Pulumi registry on left side with (M) icon:
/// - [AWS](https://www.pulumi.com/registry/packages/aws/)
/// - [Azure](https://www.pulumi.com/registry/packages/azure/)
/// - [GCP](https://www.pulumi.com/registry/packages/gcp/)
pub fn generate_with_filter(
    provider_name: &str,
    provider_version: &str,
    modules: &[&str],
) -> Result<()> {
    generate_with_optional_filter(provider_name, provider_version, Some(modules))
}

/// Generates glue code for given provider and version. Can be included using [`pulumi_wasm_rust::include_provider!(provider_name)`]
pub fn generate(provider_name: &str, provider_version: &str) -> Result<()> {
    generate_with_optional_filter(provider_name, provider_version, None)
}

/// Generates glue code for given schema json/yaml. Can be included using [`pulumi_wasm_rust::include_provider!(provider_name)`]
pub fn generate_from_schema(schema_file: &Path) -> Result<()> {
    generate_from_schema_with_optional_filter(schema_file, None)
}

/// Generates glue code for given schema json/yaml and modules. Can be included using [`pulumi_wasm_rust::include_provider!(provider_name)`]
/// Modules for provider can be found in Pulumi registry on left side with (M) icon:
/// - [AWS](https://www.pulumi.com/registry/packages/aws/)
/// - [Azure](https://www.pulumi.com/registry/packages/azure/)
/// - [GCP](https://www.pulumi.com/registry/packages/gcp/)
pub fn generate_from_schema_with_filter(schema_file: &Path, modules: &[&str]) -> Result<()> {
    generate_from_schema_with_optional_filter(schema_file, Some(modules))
}

fn generate_from_schema_with_optional_filter(
    schema_file: &Path,
    modules: Option<&[&str]>,
) -> Result<()> {
    let package = extract_micro_package(schema_file).context("Failed to deserialize package")?;
    let provider_name = package.name;

    let out_dir = env::var_os("OUT_DIR").context("Failed to get OUT_DIR environment variable")?;
    let out_dir = out_dir
        .to_str()
        .context(format!("Failed to convert [{:?}] to string", out_dir))?;
    let location = Path::new(out_dir).join("pulumi").join(provider_name);

    generate_combined(schema_file, &location, modules).context("Failed to generate glue files")?;
    println!("cargo::rerun-if-changed=build.rs");
    println!("cargo::rerun-if-changed={}", schema_file.display());
    Ok(())
}

fn generate_with_optional_filter(
    provider_name: &str,
    provider_version: &str,
    modules: Option<&[&str]>,
) -> Result<()> {
    let schema_output = std::process::Command::new("pulumi")
        .arg("package")
        .arg("get-schema")
        .arg(format!("{}@{}", provider_name, provider_version))
        .env("PULUMI_AWS_MINIMAL_SCHEMA", "true") // https://github.com/pulumi/pulumi-aws/issues/2565
        .output()
        .context("Failed to execute pulumi command")?;

    let schema = String::from_utf8(schema_output.stdout).expect("Invalid UTF-8 in pulumi output");

    let out_dir = env::var_os("OUT_DIR").context("Failed to get OUT_DIR environment variable")?;
    let out_dir = out_dir
        .to_str()
        .context(format!("Failed to convert [{:?}] to string", out_dir))?;

    let location = Path::new(out_dir).join("pulumi").join(provider_name);

    let temp_dir = tempfile::tempdir().context("Failed to create temporary directory")?;
    let file = temp_dir.path().join("schema.json");
    fs::write(&file, &schema).context("Failed to write schema")?;

    generate_combined(file.as_path(), &location, modules)
        .context("Failed to generate glue files")?;
    println!("cargo::rerun-if-changed=build.rs");

    Ok(())
}