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