canic_core/ops/rpc/
mod.rs

1pub(crate) mod methods;
2mod request;
3mod response;
4
5pub use request::*;
6pub use response::*;
7
8use crate::{
9    Error, ThisError,
10    cdk::candid::CandidType,
11    ops::{OpsError, ic::call::Call, storage::env::EnvOps},
12};
13use serde::de::DeserializeOwned;
14
15///
16/// Rpc
17/// Typed RPC command binding a request variant to its response payload.
18///
19
20pub trait Rpc {
21    type Response: CandidType + DeserializeOwned;
22
23    fn into_request(self) -> Request;
24    fn try_from_response(resp: Response) -> Result<Self::Response, RequestOpsError>;
25}
26
27///
28/// RpcOpsError
29///
30
31#[derive(Debug, ThisError)]
32pub enum RpcOpsError {
33    #[error("cannot find the root canister")]
34    RootNotFound,
35
36    #[error(transparent)]
37    RequestOpsError(#[from] request::RequestOpsError),
38}
39
40impl From<RpcOpsError> for Error {
41    fn from(err: RpcOpsError) -> Self {
42        OpsError::from(err).into()
43    }
44}
45
46// execute_rpc
47async fn execute_rpc<R: Rpc>(rpc: R) -> Result<R::Response, Error> {
48    let root_pid = EnvOps::try_get_root_pid().map_err(|_| RpcOpsError::RootNotFound)?;
49
50    let call_response = Call::unbounded_wait(root_pid, methods::CANIC_RESPONSE)
51        .with_arg(rpc.into_request())
52        .await?;
53
54    let response = call_response.candid::<Result<Response, Error>>()??;
55
56    R::try_from_response(response)
57        .map_err(RpcOpsError::from)
58        .map_err(Error::from)
59}