canic/ops/model/memory/directory/
app.rs

1use crate::{
2    Error, ThisError,
3    config::Config,
4    model::memory::{
5        directory::{AppDirectory, PrincipalList},
6        topology::SubnetCanisterRegistry,
7    },
8    ops::{
9        model::memory::{
10            MemoryOpsError,
11            directory::{DirectoryPageDto, DirectoryView, paginate},
12            env::EnvOps,
13        },
14        prelude::*,
15    },
16};
17use std::collections::BTreeMap;
18
19///
20/// AppDirectoryOpsError
21///
22
23#[derive(Debug, ThisError)]
24pub enum AppDirectoryOpsError {
25    #[error("canister type {0} not found in app directory")]
26    NotFound(CanisterType),
27}
28
29impl From<AppDirectoryOpsError> for Error {
30    fn from(err: AppDirectoryOpsError) -> Self {
31        MemoryOpsError::from(err).into()
32    }
33}
34
35///
36/// AppDirectoryOps
37///
38
39pub struct AppDirectoryOps;
40
41impl AppDirectoryOps {
42    /// Single source of truth: recompute on root, otherwise use stable view.
43    fn resolve_view() -> DirectoryView {
44        if EnvOps::is_root() {
45            Self::root_build_view()
46        } else {
47            AppDirectory::view()
48        }
49    }
50
51    /// Public stable API for exporting the view.
52    #[must_use]
53    pub fn export() -> DirectoryView {
54        Self::resolve_view()
55    }
56
57    /// Import a full view into stable memory.
58    pub fn import(view: DirectoryView) {
59        AppDirectory::import(view);
60    }
61
62    #[must_use]
63    pub fn page(offset: u64, limit: u64) -> DirectoryPageDto {
64        paginate(Self::resolve_view(), offset, limit)
65    }
66
67    /// Build AppDirectory from the registry.
68    #[must_use]
69    pub fn root_build_view() -> DirectoryView {
70        let cfg = Config::get();
71        let entries = SubnetCanisterRegistry::export();
72        let mut map: BTreeMap<CanisterType, PrincipalList> = BTreeMap::new();
73
74        for entry in entries {
75            let ty = entry.ty.clone();
76
77            if cfg.app_directory.contains(&ty) {
78                map.entry(ty).or_default().0.push(entry.pid);
79            }
80        }
81
82        map.into_iter().collect()
83    }
84
85    /// Fetch principals for a canister type from the current AppDirectory.
86    pub fn try_get(ty: &CanisterType) -> Result<PrincipalList, Error> {
87        let target = ty.clone();
88        let view = Self::resolve_view();
89        let entry = view
90            .into_iter()
91            .find_map(|(t, pids)| (t == target).then_some(pids))
92            .ok_or_else(|| AppDirectoryOpsError::NotFound(ty.clone()))?;
93
94        Ok(entry)
95    }
96}