canic-core 0.70.11

Canic — a canister orchestration and management toolkit for the Internet Computer
Documentation
//! Module: ops::storage::children
//!
//! Responsibility: expose deterministic direct-child cache reads and imports.
//! Does not own: topology cascade workflow, subnet registry truth, or endpoint DTOs.
//! Boundary: storage ops facade over child cache records.

mod mapper;

use crate::{
    dto::canister::CanisterInfo,
    ops::{
        ic::IcOps,
        prelude::*,
        runtime::env::EnvOps,
        storage::{children::mapper::CanisterRecordMapper, registry::subnet::SubnetRegistryOps},
    },
    storage::{
        canister::CanisterRecord,
        stable::children::{CanisterChildren, CanisterChildrenRecord},
    },
};

///
/// CanisterChildrenOps
///
/// Storage-ops facade for the direct-child cache.
///
/// Invariant: the children cache is updated only via the topology cascade workflow.
///

pub struct CanisterChildrenOps;

impl CanisterChildrenOps {
    // -------------------------------------------------------------------------
    // Lookup helpers
    // -------------------------------------------------------------------------

    #[must_use]
    pub fn get(pid: Principal) -> Option<CanisterRecord> {
        if EnvOps::is_root() {
            SubnetRegistryOps::get(pid)
        } else {
            CanisterChildren::get(pid)
        }
    }

    #[must_use]
    pub fn role_parent(pid: Principal) -> Option<(CanisterRole, Option<Principal>)> {
        Self::get(pid).map(|record| (record.role, record.parent_pid))
    }

    #[must_use]
    pub fn contains_pid(pid: &Principal) -> bool {
        if EnvOps::is_root() {
            SubnetRegistryOps::children(IcOps::canister_self())
                .iter()
                .any(|(child_pid, _)| child_pid == pid)
        } else {
            Self::data().entries.iter().any(|(p, _)| p == pid)
        }
    }

    #[must_use]
    pub fn infos() -> Vec<CanisterInfo> {
        Self::records()
            .into_iter()
            .map(|(pid, record)| CanisterRecordMapper::record_to_response(pid, record))
            .collect()
    }

    #[must_use]
    fn records() -> Vec<(Principal, CanisterRecord)> {
        if EnvOps::is_root() {
            SubnetRegistryOps::children(IcOps::canister_self())
        } else {
            Self::data().entries
        }
    }

    #[must_use]
    pub fn pids() -> Vec<Principal> {
        Self::records().into_iter().map(|(pid, _)| pid).collect()
    }

    // -------------------------------------------------------------------------
    // Canonical data access
    // -------------------------------------------------------------------------

    #[must_use]
    pub fn data() -> CanisterChildrenRecord {
        CanisterChildren::export()
    }

    pub(crate) fn import_direct_children(
        parent_pid: Principal,
        children: Vec<(Principal, CanisterRole)>,
    ) {
        // Cache entries omit module hash/created_at; canonical data lives in the registry.
        let data = CanisterChildrenRecord {
            entries: children
                .into_iter()
                .map(|(pid, role)| {
                    (
                        pid,
                        CanisterRecord {
                            role,
                            parent_pid: Some(parent_pid),
                            module_hash: None,
                            created_at: 0,
                        },
                    )
                })
                .collect(),
        };

        CanisterChildren::import(data);
    }
}