canic 0.65.1

Canic — a canister orchestration and management toolkit for the Internet Computer
Documentation
//! Facade macros for downstream canister crates.
mod build;
mod endpoints;
mod start;
mod timer;

// -----------------------------------------------------------------------------
// Protected internal endpoint descriptor macro
// -----------------------------------------------------------------------------

/// Define a shared descriptor for a protected Canic internal update endpoint.
///
/// Endpoint implementations generated by `#[canic_update(...)]` emit their own
/// `canic_internal_endpoint_<endpoint>()` descriptor. This macro is for shared
/// protocol modules that need to publish the same descriptor to a separate
/// caller crate without depending on the target canister implementation crate.
#[macro_export]
macro_rules! canic_protected_endpoint {
    () => {};
    (
        $(#[$meta:meta])*
        $vis:vis fn $name:ident = $method:expr, role = $role:expr;
        $($rest:tt)*
    ) => {
        $(#[$meta])*
        $vis fn $name() -> $crate::api::ic::ProtectedInternalEndpoint {
            $crate::api::ic::ProtectedInternalEndpoint::new($method, [$role])
        }

        $crate::canic_protected_endpoint! {
            $($rest)*
        }
    };
    (
        $(#[$meta:meta])*
        $vis:vis fn $name:ident = $method:expr, roles = [$($role:expr),+ $(,)?];
        $($rest:tt)*
    ) => {
        $(#[$meta])*
        $vis fn $name() -> $crate::api::ic::ProtectedInternalEndpoint {
            $crate::api::ic::ProtectedInternalEndpoint::new($method, [$($role),+])
        }

        $crate::canic_protected_endpoint! {
            $($rest)*
        }
    };
    (
        $(#[$meta:meta])*
        $vis:vis fn $name:ident = $method:expr, roles = [] $($tail:tt)*
    ) => {
        compile_error!(
            "canic_protected_endpoint! roles = [] is invalid; protected internal endpoints must accept at least one caller role"
        );
    };
    (
        $(#[$meta:meta])*
        $vis:vis fn $name:ident = $method:expr, role = $role:expr $(;)?
    ) => {
        $(#[$meta])*
        $vis fn $name() -> $crate::api::ic::ProtectedInternalEndpoint {
            $crate::api::ic::ProtectedInternalEndpoint::new($method, [$role])
        }
    };
    (
        $(#[$meta:meta])*
        $vis:vis fn $name:ident = $method:expr, roles = [$($role:expr),+ $(,)?] $(;)?
    ) => {
        $(#[$meta])*
        $vis fn $name() -> $crate::api::ic::ProtectedInternalEndpoint {
            $crate::api::ic::ProtectedInternalEndpoint::new($method, [$($role),+])
        }
    };
}

// -----------------------------------------------------------------------------
// Protected internal client macro
// -----------------------------------------------------------------------------

/// Generate a typed client for protected Canic internal update endpoints.
///
/// Each method is bound to a `ProtectedInternalEndpoint` descriptor function.
/// For endpoints generated by `#[canic_update(internal, requires(caller::has_role(...)))]`,
/// the descriptor function name is `canic_internal_endpoint_<endpoint_name>`.
#[macro_export]
macro_rules! canic_internal_client {
    (
        $(#[$client_meta:meta])*
        $vis:vis struct $client:ident {
            $(
                $(#[$method_meta:meta])*
                fn $method:ident = $endpoint:expr $(, role = $role:expr)?; (
                    $($arg:ident : $arg_ty:ty),* $(,)?
                ) -> $response:ty;
            )*
        }
    ) => {
        $(#[$client_meta])*
        $vis struct $client {
            inner: $crate::api::ic::CanicInternalClient,
        }

        impl $client {
            #[must_use]
            $vis const fn new(canister_id: $crate::cdk::types::Principal) -> Self {
                Self {
                    inner: $crate::api::ic::CanicInternalClient::new(canister_id),
                }
            }

            #[must_use]
            $vis const fn with_options(
                mut self,
                options: $crate::api::ic::CanicInternalCallOptions,
            ) -> Self {
                self.inner = self.inner.with_options(options);
                self
            }

            #[must_use]
            $vis const fn with_bounded_wait(mut self) -> Self {
                self.inner = self.inner.with_bounded_wait();
                self
            }

            #[must_use]
            $vis const fn with_unbounded_wait(mut self) -> Self {
                self.inner = self.inner.with_unbounded_wait();
                self
            }

            #[must_use]
            $vis const fn with_cycles(mut self, cycles: u128) -> Self {
                self.inner = self.inner.with_cycles(cycles);
                self
            }

            #[must_use]
            $vis const fn with_proof_ttl_secs(mut self, ttl_secs: u64) -> Self {
                self.inner = self.inner.with_proof_ttl_secs(ttl_secs);
                self
            }

            $(
                $(#[$method_meta])*
                $vis async fn $method(
                    &self,
                    $($arg: $arg_ty),*
                ) -> ::core::result::Result<$response, $crate::Error> {
                    let endpoint = ($endpoint)();
                    $crate::__canic_internal_client_execute!(
                        self,
                        endpoint,
                        ($($arg,)*),
                        $response
                        $(, $role)?
                    ).await
                }
            )*
        }
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! __canic_internal_client_execute {
    ($self:ident, $endpoint:ident, ($($arg:expr,)*), $response:ty) => {
        $self.inner.call_update_result_with_single_role::<$response, _>(
            &$endpoint,
            ($($arg,)*),
        )
    };
    ($self:ident, $endpoint:ident, ($($arg:expr,)*), $response:ty, $role:expr) => {
        $self.inner.call_update_result::<$response, _>(
            &$endpoint,
            $role,
            ($($arg,)*),
        )
    };
}

// -----------------------------------------------------------------------------
// Log macro
// -----------------------------------------------------------------------------

/// Log a runtime entry using Canic's structured logger.
#[macro_export]
macro_rules! log {
    ($($tt:tt)*) => {{
        $crate::__internal::core::log!($($tt)*);
    }};
}

// -----------------------------------------------------------------------------
// Perf macro
// -----------------------------------------------------------------------------

/// Record and log elapsed instruction counts since the last `perf!` invocation
/// in this thread.
///
/// - Uses a thread-local `PERF_LAST` snapshot.
/// - Computes `delta = now - last`.
/// - Records a structured checkpoint row in the shared perf table.
/// - Prints a human-readable line for debugging.
///
/// Intended usage:
/// - Long-running maintenance tasks where you want *checkpoints* in a single call.
///
/// Note: `perf!` is independent of endpoint perf scopes and does not touch the
/// endpoint stack used by dispatch. It records checkpoint rows keyed by
/// `module_path!()` plus the formatted label.
///
/// Notes:
/// - On non-wasm targets, `perf_counter()` returns 0, so this becomes a no-op-ish
///   counter (still records 0 deltas); this keeps unit tests compiling cleanly.
#[macro_export]
macro_rules! perf {
    ($($label:tt)*) => {{
        $crate::__internal::core::perf::PERF_LAST.with(|last| {
            // Use the wrapper so non-wasm builds compile.
            let now = $crate::__internal::core::perf::perf_counter();
            let then = *last.borrow();
            let delta = now.saturating_sub(then);

            // Update last checkpoint.
            *last.borrow_mut() = now;

            // Format label + pretty-print counters.
            let label = format!($($label)*);
            let delta_fmt = $crate::__internal::instructions::format_instructions(delta);
            let now_fmt = $crate::__internal::instructions::format_instructions(now);

            $crate::__internal::core::perf::record_checkpoint(module_path!(), &label, delta);

            $crate::__internal::core::log!(
                Info,
                Topic::Perf,
                "{}: '{}' used {}i since last (total: {}i)",
                module_path!(),
                label,
                delta_fmt,
                now_fmt
            );
        });
    }};
}