canic_core/macros/
start.rs

1/// Configure lifecycle hooks for **non-root** Canic canisters.
2///
3/// This macro defines the IC-required `init` and `post_upgrade` entry points
4/// at the crate root and *immediately delegates* all real work to runtime
5/// bootstrap code.
6///
7/// IMPORTANT:
8/// - This macro must remain **thin**
9/// - It must not perform orchestration
10/// - It must not contain async logic
11/// - It must not encode policy
12/// - It may schedule async hooks via timers, but must never await them
13///
14/// Its sole responsibility is to bridge IC lifecycle hooks to runtime code.
15#[macro_export]
16macro_rules! start {
17    ($canister_role:expr) => {
18        #[::canic::cdk::init]
19        fn init(payload: ::canic::core::dto::abi::v1::CanisterInitPayload, args: Option<Vec<u8>>) {
20            // Load embedded configuration early.
21            $crate::__canic_load_config!();
22
23            // Delegate to lifecycle adapter (NOT workflow).
24            $crate::api::lifecycle::init_nonroot_canister($canister_role, payload, args.clone());
25
26            // ---- userland lifecycle hooks (scheduled last) ----
27            $crate::api::timer::set_lifecycle_timer(
28                ::std::time::Duration::ZERO,
29                "canic:user:init",
30                async move {
31                    canic_setup().await;
32                    canic_install(args).await;
33                },
34            );
35        }
36
37        #[::canic::cdk::post_upgrade]
38        fn post_upgrade() {
39            // Reload embedded configuration on upgrade.
40            $crate::__canic_load_config!();
41
42            // Delegate to lifecycle adapter.
43            $crate::api::lifecycle::post_upgrade_nonroot_canister($canister_role);
44
45            // ---- userland lifecycle hooks (scheduled last) ----
46            $crate::api::timer::set_lifecycle_timer(
47                ::core::time::Duration::ZERO,
48                "canic:user:init",
49                async move {
50                    canic_setup().await;
51                    canic_upgrade().await;
52                },
53            );
54        }
55
56        $crate::canic_endpoints!();
57        $crate::canic_endpoints_nonroot!();
58    };
59}
60
61/// Configure lifecycle hooks for the **root orchestrator** canister.
62///
63/// This macro behaves like [`start!`], but delegates to root-specific
64/// bootstrap logic.
65///
66/// IMPORTANT:
67/// - The macro does NOT perform root orchestration
68/// - The macro does NOT import WASMs
69/// - The macro does NOT create canisters
70/// - The macro may schedule async hooks via timers, but must never await them
71///
72/// All root-specific behavior lives in `workflow::bootstrap`.
73#[macro_export]
74macro_rules! start_root {
75    () => {
76        #[::canic::cdk::init]
77        fn init(identity: ::canic::core::dto::subnet::SubnetIdentity) {
78            // Load embedded configuration early.
79            $crate::__canic_load_config!();
80
81            // Delegate to lifecycle adapter.
82            $crate::api::lifecycle::init_root_canister(identity);
83
84            // ---- userland lifecycle hooks (scheduled last) ----
85            $crate::api::timer::set_lifecycle_timer(
86                ::core::time::Duration::ZERO,
87                "canic:user:init",
88                async move {
89                    canic_setup().await;
90                    canic_install().await;
91                },
92            );
93        }
94
95        #[::canic::cdk::post_upgrade]
96        fn post_upgrade() {
97            // Reload embedded configuration on upgrade.
98            $crate::__canic_load_config!();
99
100            // Delegate to lifecycle adapter.
101            $crate::api::lifecycle::post_upgrade_root_canister();
102
103            // ---- userland lifecycle hooks (scheduled last) ----
104            $crate::api::timer::set_lifecycle_timer(
105                ::core::time::Duration::ZERO,
106                "canic:user:init",
107                async move {
108                    canic_setup().await;
109                    canic_upgrade().await;
110                },
111            );
112        }
113
114        $crate::canic_endpoints!();
115        $crate::canic_endpoints_root!();
116    };
117}
118
119//
120// Private helpers
121//
122
123///
124/// Load the embedded configuration during init and upgrade hooks.
125///
126/// This macro exists solely to embed and load the TOML configuration file
127/// at compile time (`CANIC_CONFIG_PATH`). It is used internally by
128/// [`macro@canic::start`] and [`macro@canic::start_root`].
129
130#[doc(hidden)]
131#[macro_export]
132macro_rules! __canic_load_config {
133    () => {{
134        let config_str = include_str!(env!("CANIC_CONFIG_PATH"));
135        if let Err(err) = $crate::init_config(config_str) {
136            $crate::cdk::println!(
137                "[canic] FATAL: config init failed (CANIC_CONFIG_PATH={}): {err}",
138                env!("CANIC_CONFIG_PATH")
139            );
140
141            let msg = format!(
142                "canic init failed: config init failed (CANIC_CONFIG_PATH={}): {err}",
143                env!("CANIC_CONFIG_PATH")
144            );
145
146            $crate::cdk::api::trap(&msg);
147        }
148    }};
149}