Skip to main content

canic_core/api/runtime/
install.rs

1use crate::{
2    InternalError, InternalErrorOrigin, cdk::types::Principal, dto::error::Error,
3    format::byte_size, ids::CanisterRole,
4};
5use async_trait::async_trait;
6use std::sync::OnceLock;
7
8///
9/// ApprovedModuleSource
10///
11
12#[derive(Clone, Debug, Eq, PartialEq)]
13pub struct ApprovedModuleSource {
14    pub source_canister: Principal,
15    pub source_label: String,
16    pub module_hash: Vec<u8>,
17    pub chunk_hashes: Vec<Vec<u8>>,
18    pub payload_size_bytes: u64,
19}
20
21impl ApprovedModuleSource {
22    /// Return the formatted module payload size for logs and status output.
23    #[must_use]
24    pub fn payload_size(&self) -> String {
25        byte_size(self.payload_size_bytes)
26    }
27}
28
29///
30/// ModuleSourceResolver
31///
32
33#[async_trait]
34pub trait ModuleSourceResolver: Send + Sync {
35    /// Resolve the currently approved install source for one canister role.
36    async fn approved_module_source(
37        &self,
38        role: &CanisterRole,
39    ) -> Result<ApprovedModuleSource, Error>;
40}
41
42static MODULE_SOURCE_RESOLVER: OnceLock<&'static dyn ModuleSourceResolver> = OnceLock::new();
43
44///
45/// ModuleSourceRuntimeApi
46///
47
48pub struct ModuleSourceRuntimeApi;
49
50impl ModuleSourceRuntimeApi {
51    /// Register the control-plane resolver used by root-owned installation flows.
52    pub fn register_module_source_resolver(resolver: &'static dyn ModuleSourceResolver) {
53        let _ = MODULE_SOURCE_RESOLVER.set(resolver);
54    }
55
56    /// Resolve the approved install source for one canister role through the registered driver.
57    pub(crate) async fn approved_module_source(
58        role: &CanisterRole,
59    ) -> Result<ApprovedModuleSource, InternalError> {
60        let resolver = MODULE_SOURCE_RESOLVER.get().ok_or_else(|| {
61            InternalError::workflow(
62                InternalErrorOrigin::Workflow,
63                "module source resolver is not registered; root/control-plane install flows are unavailable".to_string(),
64            )
65        })?;
66
67        resolver
68            .approved_module_source(role)
69            .await
70            .map_err(InternalError::public)
71    }
72}