use std::fs;
use std::path::{Path, PathBuf};
use miden_node_proto_build::rpc_api_descriptor;
use miden_note_transport_proto_build::mnt_api_descriptor;
use miette::IntoDiagnostic;
const RPC_STD_DIR: &str = "rpc/std";
const RPC_NOSTD_DIR: &str = "rpc/nostd";
const NOTE_TRANSPORT_STD_DIR: &str = "note_transport/std";
const NOTE_TRANSPORT_NOSTD_DIR: &str = "note_transport/nostd";
const RPC_STD_WRAPPER: &str = "rpc_std.rs";
const RPC_NOSTD_WRAPPER: &str = "rpc_nostd.rs";
const NOTE_TRANSPORT_STD_WRAPPER: &str = "note_transport_std.rs";
const NOTE_TRANSPORT_NOSTD_WRAPPER: &str = "note_transport_nostd.rs";
fn main() -> miette::Result<()> {
println!("cargo::rerun-if-changed=build.rs");
let out_dir = PathBuf::from(std::env::var("OUT_DIR").into_diagnostic()?);
compile_tonic_client_proto(&out_dir)?;
compile_tonic_note_transport_proto(&out_dir)?;
replace_no_std_types_in_dir(&out_dir.join(RPC_NOSTD_DIR))?;
replace_no_std_types_in_dir(&out_dir.join(NOTE_TRANSPORT_NOSTD_DIR))?;
generate_wrapper(&out_dir, RPC_STD_DIR, RPC_STD_WRAPPER)?;
generate_wrapper(&out_dir, RPC_NOSTD_DIR, RPC_NOSTD_WRAPPER)?;
generate_wrapper(&out_dir, NOTE_TRANSPORT_STD_DIR, NOTE_TRANSPORT_STD_WRAPPER)?;
generate_wrapper(&out_dir, NOTE_TRANSPORT_NOSTD_DIR, NOTE_TRANSPORT_NOSTD_WRAPPER)?;
Ok(())
}
fn compile_tonic_note_transport_proto(out_dir: &Path) -> miette::Result<()> {
let file_descriptors = mnt_api_descriptor();
let std_out = out_dir.join(NOTE_TRANSPORT_STD_DIR);
let nostd_out = out_dir.join(NOTE_TRANSPORT_NOSTD_DIR);
fs::create_dir_all(&std_out).into_diagnostic()?;
fs::create_dir_all(&nostd_out).into_diagnostic()?;
let mut prost_config = tonic_prost_build::Config::new();
prost_config.skip_debug(["AccountId", "Digest"]);
let mut web_tonic_prost_config = tonic_prost_build::Config::new();
web_tonic_prost_config.skip_debug(["AccountId", "Digest"]);
web_tonic_prost_config.btree_map(["."]);
tonic_prost_build::configure()
.build_transport(false)
.build_server(false)
.out_dir(&nostd_out)
.compile_fds_with_config(file_descriptors.clone(), web_tonic_prost_config)
.into_diagnostic()?;
tonic_prost_build::configure()
.build_server(false)
.out_dir(&std_out)
.compile_fds_with_config(file_descriptors, prost_config)
.into_diagnostic()?;
Ok(())
}
fn compile_tonic_client_proto(out_dir: &Path) -> miette::Result<()> {
let file_descriptors = rpc_api_descriptor();
let std_out = out_dir.join(RPC_STD_DIR);
let nostd_out = out_dir.join(RPC_NOSTD_DIR);
fs::create_dir_all(&std_out).into_diagnostic()?;
fs::create_dir_all(&nostd_out).into_diagnostic()?;
let mut prost_config = tonic_prost_build::Config::new();
prost_config.skip_debug(["AccountId", "Digest"]);
let mut web_tonic_prost_config = tonic_prost_build::Config::new();
web_tonic_prost_config.skip_debug(["AccountId", "Digest"]);
web_tonic_prost_config.btree_map(["."]);
tonic_prost_build::configure()
.build_transport(false)
.build_server(false)
.out_dir(&nostd_out)
.compile_fds_with_config(file_descriptors.clone(), web_tonic_prost_config)
.into_diagnostic()?;
tonic_prost_build::configure()
.build_server(false)
.out_dir(&std_out)
.compile_fds_with_config(file_descriptors, prost_config)
.into_diagnostic()?;
Ok(())
}
fn generate_wrapper(out_dir: &Path, subdir: &str, wrapper_name: &str) -> miette::Result<()> {
let dir = out_dir.join(subdir);
let mut mod_names: Vec<String> = fs::read_dir(&dir)
.into_diagnostic()?
.filter_map(|entry| {
let entry = entry.ok()?;
let name = entry.file_name().into_string().ok()?;
name.strip_suffix(".rs").map(str::to_owned)
})
.collect();
mod_names.sort();
let allow_attr = "#[allow(clippy::doc_markdown, clippy::struct_field_names, \
clippy::trivially_copy_pass_by_ref, clippy::large_enum_variant)]";
let mut wrapper = String::new();
for mod_name in &mod_names {
let mod_declaration = format!(
"{allow_attr}\n\
pub mod {mod_name} {{ include!(concat!(env!(\"OUT_DIR\"), \"/{subdir}/{mod_name}.rs\")); }}\n"
);
wrapper.push_str(&mod_declaration);
}
fs::write(out_dir.join(wrapper_name), wrapper).into_diagnostic()?;
Ok(())
}
fn replace_no_std_types_in_dir(dir: &Path) -> miette::Result<()> {
for entry in fs::read_dir(dir).into_diagnostic()? {
let entry = entry.into_diagnostic()?;
let path = entry.path();
if path.extension().is_some_and(|ext| ext == "rs") {
let content = fs::read_to_string(&path).into_diagnostic()?;
let replaced = content
.replace("std::result", "core::result")
.replace("std::marker", "core::marker");
fs::write(&path, replaced).into_diagnostic()?;
}
}
Ok(())
}