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 schedule timers
10/// - It must not perform orchestration
11/// - It must not contain async logic
12/// - It must not encode policy
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 ::canic::core::__canic_load_config!();
22
23 // Delegate to lifecycle adapter (NOT workflow).
24 ::canic::core::lifecycle::init::init_nonroot_canister(
25 $canister_role,
26 payload,
27 args.clone(),
28 );
29
30 // ---- userland lifecycle hooks (scheduled last) ----
31 ::canic::core::ops::ic::timer::TimerOps::set(
32 ::core::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 ::canic::core::__canic_load_config!();
45
46 // Delegate to lifecycle adapter.
47 ::canic::core::lifecycle::upgrade::post_upgrade_nonroot_canister($canister_role);
48
49 // ---- userland lifecycle hooks (scheduled last) ----
50 ::canic::core::ops::ic::timer::TimerOps::set(
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 ::canic::core::canic_endpoints!();
61 ::canic::core::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 does NOT schedule timers
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 ::canic::core::__canic_load_config!();
84
85 // Delegate to lifecycle adapter.
86 ::canic::core::lifecycle::init::init_root_canister(identity);
87
88 // ---- userland lifecycle hooks (scheduled last) ----
89 ::canic::core::ops::ic::timer::TimerOps::set(
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 ::canic::core::__canic_load_config!();
103
104 // Delegate to lifecycle adapter.
105 ::canic::core::lifecycle::upgrade::post_upgrade_root_canister();
106
107 // ---- userland lifecycle hooks (scheduled last) ----
108 ::canic::core::ops::ic::timer::TimerOps::set(
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 ::canic::core::canic_endpoints!();
119 ::canic::core::canic_endpoints_root!();
120 };
121}
122//
123// Private helpers
124//
125
126///
127/// Load the embedded configuration during init and upgrade hooks.
128///
129/// This macro exists solely to embed and load the TOML configuration file
130/// at compile time (`CANIC_CONFIG_PATH`). It is used internally by
131/// [`macro@canic::start`] and [`macro@canic::start_root`].
132
133#[doc(hidden)]
134#[macro_export]
135macro_rules! __canic_load_config {
136 () => {{
137 let config_str = include_str!(env!("CANIC_CONFIG_PATH"));
138 if let Err(err) = $crate::config::Config::init_from_toml(config_str) {
139 $crate::cdk::println!(
140 "[canic] FATAL: config init failed (CANIC_CONFIG_PATH={}): {err}",
141 env!("CANIC_CONFIG_PATH")
142 );
143 let msg = format!(
144 "canic init failed: config init failed (CANIC_CONFIG_PATH={}): {err}",
145 env!("CANIC_CONFIG_PATH")
146 );
147 $crate::cdk::api::trap(&msg);
148 }
149 }};
150}