canic_core/macros/
build.rs

1//! Build-script helpers that embed and validate `canic.toml`.
2
3/// Embed the shared Canic configuration into a canister crate's build script.
4///
5/// Reads the provided TOML file (relative to the crate manifest dir), validates it
6/// using [`Config`](crate::config::Config), and sets `CANIC_CONFIG_PATH` for
7/// later use by `include_str!`. Canister crates typically invoke this from
8/// `build.rs`.
9#[macro_export]
10macro_rules! build {
11    ($file:expr) => {{
12        $crate::__canic_build_internal! {
13            $file,
14            |cfg_str, cfg_path, cfg| {
15                let _ = (&cfg_str, &cfg_path, &cfg);
16            }
17        }
18    }};
19}
20
21/// Embed the shared configuration for the root orchestrator canister.
22///
23/// Performs the same validation as [`macro@build`].
24#[macro_export]
25macro_rules! build_root {
26    ($file:expr) => {{
27        $crate::__canic_build_internal! {
28            $file,
29            |_cfg_str, _cfg_path, _cfg| {}
30        }
31    }};
32}
33
34/// Internal helper shared by [`macro@build`] and [`macro@build_root`].
35#[doc(hidden)]
36#[macro_export]
37macro_rules! __canic_build_internal {
38    ($file:expr, |$cfg_str:ident, $cfg_path:ident, $cfg:ident| $body:block) => {{
39        const DEFAULT_CANIC_TOML: &str = r#"controllers = []
40app_directory = []
41
42[subnets.prime]
43"#;
44
45        let mut emitted_config_path = false;
46
47        let manifest_dir =
48            std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR must be set");
49        let $cfg_path = std::path::PathBuf::from(manifest_dir).join($file);
50        println!("cargo:rerun-if-changed={}", $cfg_path.display());
51        if let Some(parent) = $cfg_path.parent() {
52            println!("cargo:rerun-if-changed={}", parent.display());
53        }
54
55        let $cfg_str = match std::fs::read_to_string(&$cfg_path) {
56            Ok(s) => s,
57            Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
58                let out_dir = std::path::PathBuf::from(
59                    std::env::var("OUT_DIR").expect("OUT_DIR must be set"),
60                );
61                let fallback = out_dir.join("canic.default.toml");
62                std::fs::write(&fallback, DEFAULT_CANIC_TOML).expect("write default canic config");
63                println!("cargo:rustc-env=CANIC_CONFIG_PATH={}", fallback.display());
64                println!("cargo:rerun-if-changed={}", fallback.display());
65                emitted_config_path = true;
66                DEFAULT_CANIC_TOML.to_string()
67            }
68            Err(e) => panic!("Failed to read {}: {}", $cfg_path.display(), e),
69        };
70
71        // Init Config
72        let $cfg = canic::core::config::Config::init_from_toml(&$cfg_str)
73            .expect("Invalid Canic config");
74
75        // Run the extra body (per-canister or nothing)
76        $body
77
78        if !emitted_config_path {
79            let abs = $cfg_path.canonicalize().expect("canonicalize canic config path");
80            println!("cargo:rustc-env=CANIC_CONFIG_PATH={}", abs.display());
81            println!("cargo:rerun-if-changed={}", abs.display());
82        }
83    }};
84}