1mod response;
9
10pub use response::*;
11
12use crate::{
13 Error,
14 cdk::call::Call,
15 memory::{Env, topology::SubnetCanisterChildren},
16 ops::prelude::*,
17};
18use candid::encode_one;
19use thiserror::Error as ThisError;
20
21#[derive(Debug, ThisError)]
27pub enum RequestError {
28 #[error("this request is not allowed to be called on root")]
29 RootNotAllowed,
30
31 #[error("invalid response type")]
32 InvalidResponseType,
33}
34
35impl From<RequestError> for Error {
36 fn from(err: RequestError) -> Self {
37 OpsError::from(err).into()
38 }
39}
40
41#[derive(CandidType, Clone, Debug, Deserialize)]
47pub enum Request {
48 CreateCanister(CreateCanisterRequest),
49 UpgradeCanister(UpgradeCanisterRequest),
50 Cycles(CyclesRequest),
51}
52
53#[derive(CandidType, Clone, Debug, Deserialize)]
59pub struct CreateCanisterRequest {
60 pub canister_type: CanisterType,
61 pub parent: CreateCanisterParent,
62 pub extra_arg: Option<Vec<u8>>,
63}
64
65#[derive(CandidType, Clone, Debug, Deserialize)]
71pub enum CreateCanisterParent {
72 Root,
73 Caller,
74 Canister(Principal),
75 Directory(CanisterType),
76}
77
78#[derive(CandidType, Clone, Debug, Deserialize)]
84pub struct UpgradeCanisterRequest {
85 pub canister_pid: Principal,
86 pub canister_type: CanisterType,
87}
88
89#[derive(CandidType, Clone, Debug, Deserialize)]
95pub struct CyclesRequest {
96 pub cycles: u128,
97}
98
99async fn request(request: Request) -> Result<Response, Error> {
101 let root_pid = Env::try_get_root_pid()?;
102
103 let call_response = Call::unbounded_wait(root_pid, "canic_response")
104 .with_arg(&request)
105 .await?;
106
107 call_response.candid::<Result<Response, Error>>()?
108}
109
110pub async fn create_canister_request<A>(
112 canister_type: &CanisterType,
113 parent: CreateCanisterParent,
114 extra: Option<A>,
115) -> Result<CreateCanisterResponse, Error>
116where
117 A: CandidType + Send + Sync,
118{
119 let encoded = match extra {
120 Some(extra) => Some(encode_one(extra)?),
121 None => None,
122 };
123
124 let q = Request::CreateCanister(CreateCanisterRequest {
126 canister_type: canister_type.clone(),
127 parent,
128 extra_arg: encoded,
129 });
130
131 match request(q).await? {
132 Response::CreateCanister(res) => Ok(res),
133 _ => Err(OpsError::RequestError(RequestError::InvalidResponseType))?,
134 }
135}
136
137pub async fn upgrade_canister_request(
139 canister_pid: Principal,
140) -> Result<UpgradeCanisterResponse, Error> {
141 let canister = SubnetCanisterChildren::try_find_by_pid(&canister_pid)?;
143
144 let q = Request::UpgradeCanister(UpgradeCanisterRequest {
146 canister_pid: canister.pid,
147 canister_type: canister.ty,
148 });
149
150 match request(q).await? {
151 Response::UpgradeCanister(res) => Ok(res),
152 _ => Err(OpsError::RequestError(RequestError::InvalidResponseType))?,
153 }
154}
155
156pub async fn cycles_request(cycles: u128) -> Result<CyclesResponse, Error> {
158 let q = Request::Cycles(CyclesRequest { cycles });
159
160 if Env::is_root() {
161 return Err(OpsError::RequestError(RequestError::RootNotAllowed))?;
162 }
163
164 match request(q).await? {
165 Response::Cycles(res) => Ok(res),
166 _ => Err(OpsError::RequestError(RequestError::InvalidResponseType))?,
167 }
168}