canic_core/ops/storage/
env.rs

1use crate::{Error, ThisError, ops::storage::StorageOpsError};
2use crate::{
3    cdk::{api::canister_self, types::Principal},
4    ids::{CanisterRole, SubnetRole},
5    model::memory::Env,
6};
7
8pub use crate::model::memory::env::EnvData;
9
10///
11/// EnvOpsError
12///
13
14#[derive(Debug, ThisError)]
15pub enum EnvOpsError {
16    #[error("env import missing required fields: {0}")]
17    MissingFields(String),
18
19    #[error("failed to determine current canister role")]
20    CanisterRoleUnavailable,
21
22    #[error("failed to determine current parent principal")]
23    ParentPidUnavailable,
24
25    #[error("failed to determine current subnet principal")]
26    SubnetPidUnavailable,
27
28    #[error("failed to determine current subnet role")]
29    SubnetRoleUnavailable,
30
31    #[error("failed to determine current root principal")]
32    RootPidUnavailable,
33}
34
35impl From<EnvOpsError> for Error {
36    fn from(err: EnvOpsError) -> Self {
37        StorageOpsError::from(err).into()
38    }
39}
40
41///
42/// EnvOps
43///
44/// NOTE:
45/// - `try_*` getters are test-only helpers for incomplete env setup.
46/// - Non-`try_*` getters assume the environment has been fully initialized
47///   during canister startup and will panic if called earlier.
48/// - After initialization, absence of environment fields is a programmer error.
49///
50
51pub struct EnvOps;
52
53impl EnvOps {
54    // ---------------------------------------------------------------------
55    // Initialization / import
56    // ---------------------------------------------------------------------
57
58    pub fn import(env: EnvData) -> Result<(), Error> {
59        let mut missing = Vec::new();
60        if env.prime_root_pid.is_none() {
61            missing.push("prime_root_pid");
62        }
63        if env.subnet_role.is_none() {
64            missing.push("subnet_role");
65        }
66        if env.subnet_pid.is_none() {
67            missing.push("subnet_pid");
68        }
69        if env.root_pid.is_none() {
70            missing.push("root_pid");
71        }
72        if env.canister_role.is_none() {
73            missing.push("canister_role");
74        }
75        if env.parent_pid.is_none() {
76            missing.push("parent_pid");
77        }
78
79        if !missing.is_empty() {
80            return Err(EnvOpsError::MissingFields(missing.join(", ")).into());
81        }
82
83        Env::import(env);
84        Ok(())
85    }
86
87    pub fn set_prime_root_pid(pid: Principal) {
88        Env::set_prime_root_pid(pid);
89    }
90
91    pub fn set_subnet_role(role: SubnetRole) {
92        Env::set_subnet_role(role);
93    }
94
95    pub fn set_subnet_pid(pid: Principal) {
96        Env::set_subnet_pid(pid);
97    }
98
99    pub fn set_root_pid(pid: Principal) {
100        Env::set_root_pid(pid);
101    }
102
103    pub fn set_canister_role(role: CanisterRole) {
104        Env::set_canister_role(role);
105    }
106
107    // ---------------------------------------------------------------------
108    // Environment predicates
109    // ---------------------------------------------------------------------
110
111    #[must_use]
112    pub fn is_prime_root() -> bool {
113        Self::prime_root_pid() == Self::root_pid()
114    }
115
116    #[must_use]
117    pub fn is_prime_subnet() -> bool {
118        Self::subnet_role().is_prime()
119    }
120
121    #[must_use]
122    pub fn is_root() -> bool {
123        Self::root_pid() == canister_self()
124    }
125
126    // ---------------------------------------------------------------------
127    // Bootstrap / fallible accessors
128    // ---------------------------------------------------------------------
129
130    #[cfg(test)]
131    pub fn try_get_subnet_role() -> Result<SubnetRole, Error> {
132        Env::get_subnet_role().ok_or_else(|| EnvOpsError::SubnetRoleUnavailable.into())
133    }
134
135    #[cfg(test)]
136    pub fn try_get_canister_role() -> Result<CanisterRole, Error> {
137        Env::get_canister_role().ok_or_else(|| EnvOpsError::CanisterRoleUnavailable.into())
138    }
139
140    #[cfg(test)]
141    pub fn try_get_subnet_pid() -> Result<Principal, Error> {
142        Env::get_subnet_pid().ok_or_else(|| EnvOpsError::SubnetPidUnavailable.into())
143    }
144
145    #[cfg(test)]
146    pub fn try_get_root_pid() -> Result<Principal, Error> {
147        Env::get_root_pid().ok_or_else(|| EnvOpsError::RootPidUnavailable.into())
148    }
149
150    #[cfg(test)]
151    pub fn try_get_prime_root_pid() -> Result<Principal, Error> {
152        Env::get_prime_root_pid().ok_or_else(|| EnvOpsError::RootPidUnavailable.into())
153    }
154
155    #[cfg(test)]
156    pub fn try_get_parent_pid() -> Result<Principal, Error> {
157        Env::get_parent_pid().ok_or_else(|| EnvOpsError::ParentPidUnavailable.into())
158    }
159
160    // ---------------------------------------------------------------------
161    // Steady-state / infallible accessors
162    // ---------------------------------------------------------------------
163
164    #[must_use]
165    pub fn subnet_role() -> SubnetRole {
166        Env::get_subnet_role()
167            .expect("EnvOps::subnet_role called before environment initialization")
168    }
169
170    #[must_use]
171    pub fn canister_role() -> CanisterRole {
172        Env::get_canister_role()
173            .expect("EnvOps::canister_role called before environment initialization")
174    }
175
176    #[must_use]
177    pub fn subnet_pid() -> Principal {
178        Env::get_subnet_pid().expect("EnvOps::subnet_pid called before environment initialization")
179    }
180
181    #[must_use]
182    pub fn root_pid() -> Principal {
183        Env::get_root_pid().expect("EnvOps::root_pid called before environment initialization")
184    }
185
186    #[must_use]
187    pub fn prime_root_pid() -> Principal {
188        Env::get_prime_root_pid()
189            .expect("EnvOps::prime_root_pid called before environment initialization")
190    }
191
192    #[must_use]
193    pub fn parent_pid() -> Principal {
194        Env::get_parent_pid().expect("EnvOps::parent_pid called before environment initialization")
195    }
196
197    // ---------------------------------------------------------------------
198    // Export
199    // ---------------------------------------------------------------------
200
201    /// Export a snapshot of the current environment metadata.
202    #[must_use]
203    pub fn export() -> EnvData {
204        Env::export()
205    }
206}