use std::{
env, fs,
io::Write,
path::PathBuf,
process::{Command, Stdio},
};
const GSDK_API_GEN: &str = "GSDK_API_GEN";
const GSDK_API_GEN_PKG: &str = "gsdk-api-gen";
const GSDK_API_GEN_RELATIVE_PATH: &str = "gsdk-api-gen";
const VARA_RUNTIME_PKG: &str = "vara-runtime";
const VARA_RUNTIME_RELATIVE_PATH: &str = "wbuild/vara-runtime/vara_runtime.compact.compressed.wasm";
const GENERATED_API_PATH: &str = "src/metadata/generated.rs";
const ENV_RUNTIME_WASM: &str = "RUNTIME_WASM";
fn main() {
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-env-changed={}", GSDK_API_GEN);
if env::var(GSDK_API_GEN) != Ok("1".into()) {
return;
}
let generated = format!("{}/{}", env!("CARGO_MANIFEST_DIR"), GENERATED_API_PATH);
fs::write(generated, generate_api()).expect("Failed to write generated api");
}
fn generate_api() -> Vec<u8> {
let root = env!("CARGO_MANIFEST_DIR");
let profile = env::var("PROFILE").expect("Environment PROFILE not found.");
let [vara_runtime, api_gen] = [
(VARA_RUNTIME_RELATIVE_PATH, VARA_RUNTIME_PKG, vec!["dev"]),
(GSDK_API_GEN_RELATIVE_PATH, GSDK_API_GEN_PKG, vec![]),
]
.map(|(relative_path, pkg, features)| get_path(root, &profile, relative_path, pkg, features));
let code = Command::new(api_gen)
.env(ENV_RUNTIME_WASM, vara_runtime)
.output()
.expect("Failed to generate client api.")
.stdout;
format(&code).into_bytes()
}
fn format(stream: &[u8]) -> String {
let raw = String::from_utf8_lossy(stream).to_string();
let mut rustfmt = Command::new("rustfmt");
let mut code = rustfmt
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.expect("Spawn rustfmt failed");
code.stdin
.as_mut()
.expect("Get stdin of rustfmt failed")
.write_all(raw.as_bytes())
.expect("pipe generated code to rustfmt failed");
let out = code.wait_with_output().expect("Run rustfmt failed").stdout;
String::from_utf8_lossy(&out)
.to_string()
.replace(":: subxt", "::subxt")
.replace(" : :: ", ": ::")
.replace(" :: ", "::")
}
fn get_path(
root: &str,
profile: &str,
relative_path: &str,
pkg: &str,
features: Vec<&'static str>,
) -> PathBuf {
let path = PathBuf::from(format!("{}/../target/{}/{}", root, profile, relative_path));
if !path.exists() {
let mut args = ["build", "--package", pkg]
.into_iter()
.map(String::from)
.collect::<Vec<_>>();
if !features.is_empty() {
args.push("--features".into());
args.push(features.join(","));
}
if profile == "release" {
args.push("--release".into());
}
panic!(
"package {} has not been compiled yet, please run \
`cargo {}` first, or override environment `GEN_CLIENT_API` with `0` for disabling the api generation",
pkg,
args.join(" ")
);
}
path
}