canic_core/ops/rpc/
mod.rs

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