1use candid_parser::{bindings::rust, pretty_check_file, Principal};
2use std::env;
3use std::fs;
4use std::io::Write;
5use std::path::PathBuf;
6
7#[derive(Clone)]
8pub struct Config {
9 pub canister_name: String,
10 pub candid_path: PathBuf,
11 pub skip_existing_files: bool,
12 pub binding: rust::Config,
13}
14
15impl Config {
16 pub fn new(canister_name: &str) -> Self {
17 let (candid_path, canister_id) = resolve_candid_path_and_canister_id(canister_name);
18 let mut binding = rust::Config::new();
19 binding
20 .set_candid_crate("candid".to_string())
22 .set_canister_id(canister_id)
23 .set_service_name(canister_name.to_string())
24 .set_target(rust::Target::CanisterCall);
25
26 Config {
27 canister_name: canister_name.to_string(),
28 candid_path,
29 skip_existing_files: false,
30 binding,
31 }
32 }
33}
34
35fn resolve_candid_path_and_canister_id(canister_name: &str) -> (PathBuf, Principal) {
47 fn warning_deprecated_env(deprecated_name: &str, new_name: &str) {
48 println!("cargo:warning=The environment variable {} is deprecated. Please set {} instead. Upgrading dfx may fix this issue.", deprecated_name, new_name);
49 }
50
51 let canister_name = canister_name.replace('-', "_");
52 let canister_name_upper = canister_name.to_uppercase();
53
54 let candid_path_var_name = format!("CANISTER_CANDID_PATH_{}", canister_name_upper);
55 let candid_path_var_name_legacy = format!("CANISTER_CANDID_PATH_{}", canister_name);
56
57 let candid_path_str = if let Ok(candid_path_str) = env::var(&candid_path_var_name) {
58 candid_path_str
59 } else if let Ok(candid_path_str) = env::var(&candid_path_var_name_legacy) {
60 warning_deprecated_env(&candid_path_var_name_legacy, &candid_path_var_name);
61 candid_path_str
62 } else {
63 panic!(
64 "Cannot find environment variable: {}",
65 &candid_path_var_name
66 );
67 };
68 let candid_path = PathBuf::from(candid_path_str);
69
70 let canister_id_var_name = format!("CANISTER_ID_{}", canister_name_upper);
71 let canister_id_var_name_legacy = format!("CANISTER_ID_{}", canister_name);
72 let canister_id_str = if let Ok(canister_id_str) = env::var(&canister_id_var_name) {
73 canister_id_str
74 } else if let Ok(canister_id_str) = env::var(&canister_id_var_name_legacy) {
75 warning_deprecated_env(&canister_id_var_name_legacy, &canister_id_var_name);
76 canister_id_str
77 } else {
78 panic!(
79 "Cannot find environment variable: {}",
80 &canister_id_var_name
81 );
82 };
83 let canister_id = Principal::from_text(&canister_id_str)
84 .unwrap_or_else(|_| panic!("Invalid principal: {}", &canister_id_str));
85
86 (candid_path, canister_id)
87}
88
89#[derive(Default)]
90pub struct Builder {
91 configs: Vec<Config>,
92}
93
94impl Builder {
95 pub fn new() -> Self {
96 Builder {
97 configs: Vec::new(),
98 }
99 }
100 pub fn add(&mut self, config: Config) -> &mut Self {
101 self.configs.push(config);
102 self
103 }
104 pub fn build(self, out_path: Option<PathBuf>) {
105 let out_path = out_path.unwrap_or_else(|| {
106 let manifest_dir =
107 PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("Cannot find manifest dir"));
108 manifest_dir.join("src").join("declarations")
109 });
110 fs::create_dir_all(&out_path).unwrap();
111 for conf in self.configs.iter() {
112 let (env, actor) =
113 pretty_check_file(&conf.candid_path).expect("Cannot parse candid file");
114 let content = rust::compile(&conf.binding, &env, &actor);
115 let generated_path = out_path.join(format!("{}.rs", conf.canister_name));
116 if !(conf.skip_existing_files && generated_path.exists()) {
117 fs::write(generated_path, content).expect("Cannot store generated binding");
118 }
119 }
120 let mut module = fs::File::create(out_path.join("mod.rs")).unwrap();
121 module.write_all(b"#![allow(unused_imports)]\n").unwrap();
122 module
123 .write_all(b"#![allow(non_upper_case_globals)]\n")
124 .unwrap();
125 module.write_all(b"#![allow(non_snake_case)]\n").unwrap();
126 for conf in self.configs.iter() {
127 module.write_all(b"#[rustfmt::skip]\n").unwrap(); let line = format!("pub mod {};\n", conf.canister_name);
129 module.write_all(line.as_bytes()).unwrap();
130 }
131 }
132}