canic_core/macros/
start.rs

1//! # Canic Lifecycle Macros
2//!
3//! These macros define **compile-time lifecycle entry points** (`init` and `post_upgrade`)
4//! for Canic canisters. Lifecycle hooks must exist at the crate root with fixed names,
5//! so they cannot be registered dynamically — macros are therefore used to generate the
6//! boilerplate pre- and post-initialization logic automatically.
7//!
8//! Each macro sets up configuration, memory, timers, and TLS before calling user-defined
9//! async setup functions (`canic_setup`, `canic_install`, `canic_upgrade`), and then
10//! exposes the standard Canic endpoint suites.
11//!
12//! ## When to use which
13//!
14//! - [`macro@canic::start`] — for **non-root** canisters (standard services, workers, etc.).
15//! - [`macro@canic::start_root`] — for the **root orchestrator**, which performs
16//!   additional initialization for global registries and root-only extensions.
17
18/// Configure lifecycle hooks for **non-root Canic canisters**.
19///
20/// This macro wires up the `init` and `post_upgrade` entry points required by the IC,
21/// performing pre-initialization steps (config, memory, TLS, environment) before invoking
22/// user async functions:
23///
24/// ```ignore
25/// async fn canic_setup() { /* shared setup */ }
26/// async fn canic_install(args: Option<Vec<u8>>) { /* called after init */ }
27/// async fn canic_upgrade() { /* called after post_upgrade */ }
28/// ```
29///
30/// These functions are spawned asynchronously after bootstrap completes.
31/// The macro also exposes the standard non-root Canic endpoint suites.
32///
33/// This macro must be used instead of a normal function because the IC runtime requires
34/// `init` and `post_upgrade` to be declared at the top level.
35
36#[macro_export]
37macro_rules! start {
38    ($canister_type:expr) => {
39        #[::canic::cdk::init]
40        fn init(payload: ::canic::core::ops::CanisterInitPayload, args: Option<Vec<u8>>) {
41            ::canic::core::__canic_load_config!();
42
43            // ops
44            ::canic::core::ops::runtime::nonroot_init($canister_type, payload);
45
46            // timers — async body, no spawn()
47            let _ = ::canic::core::interface::ic::timer::Timer::set(
48                ::std::time::Duration::from_secs(0),
49                "startup:init",
50                async move {
51                    canic_setup().await;
52                    canic_install(args).await;
53                },
54            );
55        }
56
57        #[::canic::cdk::post_upgrade]
58        fn post_upgrade() {
59            ::canic::core::__canic_load_config!();
60
61            // ops
62            ::canic::core::ops::runtime::nonroot_post_upgrade($canister_type);
63
64            // timers — async body, no spawn()
65            let _ = ::canic::core::interface::ic::timer::Timer::set(
66                ::std::time::Duration::from_secs(0),
67                "startup:upgrade",
68                async move {
69                    canic_setup().await;
70                    canic_upgrade().await;
71                },
72            );
73        }
74
75        ::canic::core::canic_endpoints!();
76        ::canic::core::canic_endpoints_nonroot!();
77    };
78}
79
80///
81/// Configure lifecycle hooks for the **root Canic orchestrator canister**.
82///
83/// This macro behaves like [`macro@canic::start`], but includes additional
84/// root-only initialization for:
85///
86/// - the global subnet registry
87/// - root-only memory extensions and cycle tracking
88/// - the root endpoint suite
89///
90/// It generates the `init` and `post_upgrade` hooks required by the IC, loads embedded
91/// configuration, imports the root `WASMS` bundle, and runs pre- and post-upgrade logic
92/// in [`ops::runtime_lifecycle`].
93///
94/// Use this for the root orchestrator canister only. Other canisters should use
95/// [`macro@canic::start`].
96
97#[macro_export]
98macro_rules! start_root {
99    () => {
100        #[::canic::cdk::init]
101        fn init(identity: ::canic::core::model::memory::topology::SubnetIdentity) {
102            ::canic::core::__canic_load_config!();
103
104            // ops
105            ::canic::core::ops::runtime::root_init(identity);
106
107            // import wasms
108            ::canic::core::ops::wasm::WasmOps::import_static(WASMS);
109
110            // timers
111            let _ = ::canic::core::interface::ic::timer::Timer::set(
112                std::time::Duration::from_secs(0),
113                "startup:root",
114                async move {
115                    ::canic::core::ops::root::root_set_subnet_id().await;
116
117                    // attempt to create canisters
118                    if let Err(err) = ::canic::core::ops::root::root_create_canisters().await {
119                        $crate::log!(
120                            $crate::log::Topic::Init,
121                            Error,
122                            "root_create_canisters failed: {err}"
123                        );
124                        return;
125                    }
126
127                    canic_setup().await;
128                    canic_install().await;
129                },
130            );
131        }
132
133        #[::canic::cdk::post_upgrade]
134        fn post_upgrade() {
135            ::canic::core::__canic_load_config!();
136            ::canic::core::ops::wasm::WasmOps::import_static(WASMS);
137
138            // ops
139            ::canic::core::ops::runtime::root_post_upgrade();
140
141            // timers
142            let _ = ::canic::core::interface::ic::timer::Timer::set(
143                ::std::time::Duration::from_secs(0),
144                "startup:root-upgrade",
145                async move {
146                    canic_setup().await;
147                    canic_upgrade().await;
148                },
149            );
150        }
151
152        ::canic::core::canic_endpoints!();
153        ::canic::core::canic_endpoints_root!();
154    };
155}
156
157//
158// Private helpers
159//
160
161///
162/// Load the embedded configuration during init and upgrade hooks.
163///
164/// This macro exists solely to embed and load the TOML configuration file
165/// at compile time (`CANIC_CONFIG_PATH`). It is used internally by
166/// [`macro@canic::start`] and [`macro@canic::start_root`].
167
168#[doc(hidden)]
169#[macro_export]
170macro_rules! __canic_load_config {
171    () => {
172        #[cfg(canic)]
173        {
174            let config_str = include_str!(env!("CANIC_CONFIG_PATH"));
175            $crate::config::Config::init_from_toml(config_str).unwrap();
176        }
177    };
178}