canic_core/ops/rpc/
request.rs

1use super::{Rpc, RpcOpsError};
2use crate::{
3    Error, ThisError,
4    dto::rpc::{
5        CreateCanisterParent, CreateCanisterRequest, CreateCanisterResponse, CyclesRequest,
6        CyclesResponse, Request, Response, UpgradeCanisterRequest, UpgradeCanisterResponse,
7    },
8    ops::{prelude::*, runtime::env::EnvOps},
9};
10use candid::encode_one;
11
12///
13/// RequestOpsError
14/// Errors produced during request dispatch or response handling
15///
16
17#[derive(Debug, ThisError)]
18pub enum RequestOpsError {
19    #[error("canister role {0} not found")]
20    CanisterRoleNotFound(CanisterRole),
21
22    #[error("child canister {0} not found")]
23    ChildNotFound(Principal),
24
25    #[error("invalid response type")]
26    InvalidResponseType,
27
28    #[error("create_canister: missing new pid")]
29    MissingNewCanisterPid,
30
31    #[error("canister {0} is not a child of caller {1}")]
32    NotChildOfCaller(Principal, Principal),
33
34    #[error("canister {0}'s parent was not found")]
35    ParentNotFound(Principal),
36}
37
38impl From<RequestOpsError> for Error {
39    fn from(err: RequestOpsError) -> Self {
40        RpcOpsError::from(err).into()
41    }
42}
43
44///
45/// CreateCanister
46///
47
48pub async fn create_canister_request<A>(
49    canister_role: &CanisterRole,
50    parent: CreateCanisterParent,
51    extra: Option<A>,
52) -> Result<CreateCanisterResponse, Error>
53where
54    A: CandidType + Send + Sync,
55{
56    let extra_arg = extra.map(encode_one).transpose()?;
57
58    super::execute_rpc(CreateCanisterRpc {
59        canister_role: canister_role.clone(),
60        parent,
61        extra_arg,
62    })
63    .await
64}
65
66///
67/// CreateCanisterRpc
68///
69
70pub struct CreateCanisterRpc {
71    pub canister_role: CanisterRole,
72    pub parent: CreateCanisterParent,
73    pub extra_arg: Option<Vec<u8>>,
74}
75
76impl Rpc for CreateCanisterRpc {
77    type Response = CreateCanisterResponse;
78
79    fn into_request(self) -> Request {
80        Request::CreateCanister(CreateCanisterRequest {
81            canister_role: self.canister_role,
82            parent: self.parent,
83            extra_arg: self.extra_arg,
84        })
85    }
86
87    fn try_from_response(resp: Response) -> Result<Self::Response, RequestOpsError> {
88        match resp {
89            Response::CreateCanister(r) => Ok(r),
90            _ => Err(RequestOpsError::InvalidResponseType),
91        }
92    }
93}
94
95///
96/// UpgradeCanister
97/// Ask root to upgrade a child canister to its latest registered WASM.
98///
99
100pub async fn upgrade_canister_request(
101    canister_pid: Principal,
102) -> Result<UpgradeCanisterResponse, Error> {
103    super::execute_rpc(UpgradeCanisterRpc { canister_pid }).await
104}
105
106pub struct UpgradeCanisterRpc {
107    pub canister_pid: Principal,
108}
109
110impl Rpc for UpgradeCanisterRpc {
111    type Response = UpgradeCanisterResponse;
112
113    fn into_request(self) -> Request {
114        Request::UpgradeCanister(UpgradeCanisterRequest {
115            canister_pid: self.canister_pid,
116        })
117    }
118
119    fn try_from_response(resp: Response) -> Result<Self::Response, RequestOpsError> {
120        match resp {
121            Response::UpgradeCanister(r) => Ok(r),
122            _ => Err(RequestOpsError::InvalidResponseType),
123        }
124    }
125}
126
127///
128/// Cycles
129/// Request a cycle transfer from root to the current canister.
130///
131
132pub async fn cycles_request(cycles: u128) -> Result<CyclesResponse, Error> {
133    EnvOps::deny_root()?;
134
135    super::execute_rpc(CyclesRpc { cycles }).await
136}
137
138pub struct CyclesRpc {
139    pub cycles: u128,
140}
141
142impl Rpc for CyclesRpc {
143    type Response = CyclesResponse;
144
145    fn into_request(self) -> Request {
146        Request::Cycles(CyclesRequest {
147            cycles: self.cycles,
148        })
149    }
150
151    fn try_from_response(resp: Response) -> Result<Self::Response, RequestOpsError> {
152        match resp {
153            Response::Cycles(r) => Ok(r),
154            _ => Err(RequestOpsError::InvalidResponseType),
155        }
156    }
157}