use async_trait::async_trait;
use candid::{decode_args, decode_one, utils::ArgumentDecoder, CandidType};
use garcon::Waiter;
use ic_agent::{agent::UpdateBuilder, export::Principal, Agent, AgentError, RequestId};
use serde::de::DeserializeOwned;
use std::fmt;
use std::future::Future;
mod expiry;
pub use expiry::Expiry;
#[async_trait]
pub trait SyncCall<O>
where
O: for<'de> ArgumentDecoder<'de> + Send,
{
#[cfg(feature = "raw")]
async fn call_raw(self) -> Result<Vec<u8>, AgentError>;
async fn call(self) -> Result<O, AgentError>
where
Self: Sized + Send,
O: 'async_trait;
}
#[async_trait]
pub trait AsyncCall<Out>
where
Out: for<'de> ArgumentDecoder<'de> + Send,
{
async fn call(self) -> Result<RequestId, AgentError>;
async fn call_and_wait<W>(self, mut waiter: W) -> Result<Out, AgentError>
where
W: Waiter;
fn and_then<Out2, R, AndThen>(
self,
and_then: AndThen,
) -> AndThenAsyncCaller<Out, Out2, Self, R, AndThen>
where
Self: Sized + Send,
Out2: for<'de> ArgumentDecoder<'de> + Send,
R: Future<Output = Result<Out2, AgentError>> + Send,
AndThen: Send + Fn(Out) -> R,
{
AndThenAsyncCaller::new(self, and_then)
}
fn map<Out2, Map>(self, map: Map) -> MappedAsyncCaller<Out, Out2, Self, Map>
where
Self: Sized + Send,
Out2: for<'de> ArgumentDecoder<'de> + Send,
Map: Send + Fn(Out) -> Out2,
{
MappedAsyncCaller::new(self, map)
}
}
#[derive(Debug)]
pub struct SyncCaller<'agent, Out>
where
Out: for<'de> ArgumentDecoder<'de> + Send,
{
pub(crate) agent: &'agent Agent,
pub(crate) effective_canister_id: Principal,
pub(crate) canister_id: Principal,
pub(crate) method_name: String,
pub(crate) arg: Result<Vec<u8>, AgentError>,
pub(crate) expiry: Expiry,
pub(crate) phantom_out: std::marker::PhantomData<Out>,
}
impl<'agent, Out> SyncCaller<'agent, Out>
where
Out: for<'de> ArgumentDecoder<'de> + Send,
{
async fn call_raw(self) -> Result<Vec<u8>, AgentError> {
let mut builder = self.agent.query(&self.canister_id, &self.method_name);
self.expiry.apply_to_query(&mut builder);
builder.with_arg(&self.arg?);
builder.with_effective_canister_id(self.effective_canister_id);
builder.call().await
}
}
#[async_trait]
impl<'agent, Out> SyncCall<Out> for SyncCaller<'agent, Out>
where
Self: Sized,
Out: 'agent + for<'de> ArgumentDecoder<'de> + Send,
{
#[cfg(feature = "raw")]
async fn call_raw(self) -> Result<Vec<u8>, AgentError> {
Ok(self.call_raw().await?)
}
async fn call(self) -> Result<Out, AgentError> {
let result = self.call_raw().await?;
decode_args(&result).map_err(|e| AgentError::CandidError(Box::new(e)))
}
}
#[derive(Debug)]
pub struct AsyncCaller<'agent, Out>
where
Out: for<'de> ArgumentDecoder<'de> + Send,
{
pub(crate) agent: &'agent Agent,
pub(crate) effective_canister_id: Principal,
pub(crate) canister_id: Principal,
pub(crate) method_name: String,
pub(crate) arg: Result<Vec<u8>, AgentError>,
pub(crate) expiry: Expiry,
pub(crate) phantom_out: std::marker::PhantomData<Out>,
}
impl<'agent, Out> AsyncCaller<'agent, Out>
where
Out: for<'de> ArgumentDecoder<'de> + Send,
{
pub fn build_call(self) -> Result<UpdateBuilder<'agent>, AgentError> {
let mut builder = self.agent.update(&self.canister_id, &self.method_name);
self.expiry.apply_to_update(&mut builder);
builder.with_arg(&self.arg?);
builder.with_effective_canister_id(self.effective_canister_id);
Ok(builder)
}
pub async fn call(self) -> Result<RequestId, AgentError> {
self.build_call()?.call().await
}
pub async fn call_and_wait<W>(self, waiter: W) -> Result<Out, AgentError>
where
W: Waiter,
{
self.build_call()?
.call_and_wait(waiter)
.await
.and_then(|r| decode_args(&r).map_err(|e| AgentError::CandidError(Box::new(e))))
}
pub async fn call_and_wait_one<W, T>(self, waiter: W) -> Result<T, AgentError>
where
W: Waiter,
T: DeserializeOwned + CandidType,
{
self.build_call()?
.call_and_wait(waiter)
.await
.and_then(|r| decode_one(&r).map_err(|e| AgentError::CandidError(Box::new(e))))
}
pub fn map<Out2, Map>(self, map: Map) -> MappedAsyncCaller<Out, Out2, Self, Map>
where
Out2: for<'de> ArgumentDecoder<'de> + Send,
Map: Send + Fn(Out) -> Out2,
{
MappedAsyncCaller::new(self, map)
}
}
#[async_trait]
impl<'agent, Out> AsyncCall<Out> for AsyncCaller<'agent, Out>
where
Out: for<'de> ArgumentDecoder<'de> + Send,
{
async fn call(self) -> Result<RequestId, AgentError> {
self.call().await
}
async fn call_and_wait<W>(self, waiter: W) -> Result<Out, AgentError>
where
W: Waiter,
{
self.call_and_wait(waiter).await
}
}
pub struct AndThenAsyncCaller<
Out: for<'de> ArgumentDecoder<'de> + Send,
Out2: for<'de> ArgumentDecoder<'de> + Send,
Inner: AsyncCall<Out> + Send,
R: Future<Output = Result<Out2, AgentError>> + Send,
AndThen: Send + Fn(Out) -> R,
> {
inner: Inner,
and_then: AndThen,
_out: std::marker::PhantomData<Out>,
_out2: std::marker::PhantomData<Out2>,
}
impl<Out, Out2, Inner, R, AndThen> fmt::Debug for AndThenAsyncCaller<Out, Out2, Inner, R, AndThen>
where
Out: for<'de> ArgumentDecoder<'de> + Send,
Out2: for<'de> ArgumentDecoder<'de> + Send,
Inner: AsyncCall<Out> + Send + fmt::Debug,
R: Future<Output = Result<Out2, AgentError>> + Send,
AndThen: Send + Fn(Out) -> R + fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("AndThenAsyncCaller")
.field("inner", &self.inner)
.field("and_then", &self.and_then)
.field("_out", &self._out)
.field("_out2", &self._out2)
.finish()
}
}
impl<Out, Out2, Inner, R, AndThen> AndThenAsyncCaller<Out, Out2, Inner, R, AndThen>
where
Out: for<'de> ArgumentDecoder<'de> + Send,
Out2: for<'de> ArgumentDecoder<'de> + Send,
Inner: AsyncCall<Out> + Send,
R: Future<Output = Result<Out2, AgentError>> + Send,
AndThen: Send + Fn(Out) -> R,
{
pub fn new(inner: Inner, and_then: AndThen) -> Self {
Self {
inner,
and_then,
_out: std::marker::PhantomData,
_out2: std::marker::PhantomData,
}
}
pub async fn call(self) -> Result<RequestId, AgentError> {
self.inner.call().await
}
pub async fn call_and_wait<W>(self, waiter: W) -> Result<Out2, AgentError>
where
W: Waiter,
{
let v = self.inner.call_and_wait(waiter).await?;
let f = (self.and_then)(v);
f.await
}
pub fn and_then<Out3, R2, AndThen2>(
self,
and_then: AndThen2,
) -> AndThenAsyncCaller<Out2, Out3, Self, R2, AndThen2>
where
Out3: for<'de> ArgumentDecoder<'de> + Send,
R2: Future<Output = Result<Out3, AgentError>> + Send,
AndThen2: Send + Fn(Out2) -> R2,
{
AndThenAsyncCaller::new(self, and_then)
}
pub fn map<Out3, Map>(self, map: Map) -> MappedAsyncCaller<Out2, Out3, Self, Map>
where
Out3: for<'de> ArgumentDecoder<'de> + Send,
Map: Send + Fn(Out2) -> Out3,
{
MappedAsyncCaller::new(self, map)
}
}
#[async_trait]
impl<Out, Out2, Inner, R, AndThen> AsyncCall<Out2>
for AndThenAsyncCaller<Out, Out2, Inner, R, AndThen>
where
Out: for<'de> ArgumentDecoder<'de> + Send,
Out2: for<'de> ArgumentDecoder<'de> + Send,
Inner: AsyncCall<Out> + Send,
R: Future<Output = Result<Out2, AgentError>> + Send,
AndThen: Send + Fn(Out) -> R,
{
async fn call(self) -> Result<RequestId, AgentError> {
self.call().await
}
async fn call_and_wait<W>(self, waiter: W) -> Result<Out2, AgentError>
where
W: Waiter,
{
self.call_and_wait(waiter).await
}
}
pub struct MappedAsyncCaller<
Out: for<'de> ArgumentDecoder<'de> + Send,
Out2: for<'de> ArgumentDecoder<'de> + Send,
Inner: AsyncCall<Out> + Send,
Map: Send + Fn(Out) -> Out2,
> {
inner: Inner,
map: Map,
_out: std::marker::PhantomData<Out>,
_out2: std::marker::PhantomData<Out2>,
}
impl<Out, Out2, Inner, Map> fmt::Debug for MappedAsyncCaller<Out, Out2, Inner, Map>
where
Out: for<'de> ArgumentDecoder<'de> + Send,
Out2: for<'de> ArgumentDecoder<'de> + Send,
Inner: AsyncCall<Out> + Send + fmt::Debug,
Map: Send + Fn(Out) -> Out2 + fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("MappedAsyncCaller")
.field("inner", &self.inner)
.field("map", &self.map)
.field("_out", &self._out)
.field("_out2", &self._out2)
.finish()
}
}
impl<Out, Out2, Inner, Map> MappedAsyncCaller<Out, Out2, Inner, Map>
where
Out: for<'de> ArgumentDecoder<'de> + Send,
Out2: for<'de> ArgumentDecoder<'de> + Send,
Inner: AsyncCall<Out> + Send,
Map: Send + Fn(Out) -> Out2,
{
pub fn new(inner: Inner, map: Map) -> Self {
Self {
inner,
map,
_out: std::marker::PhantomData,
_out2: std::marker::PhantomData,
}
}
pub async fn call(self) -> Result<RequestId, AgentError> {
self.inner.call().await
}
pub async fn call_and_wait<W>(self, waiter: W) -> Result<Out2, AgentError>
where
W: Waiter,
{
let v = self.inner.call_and_wait(waiter).await?;
Ok((self.map)(v))
}
pub fn and_then<Out3, R2, AndThen2>(
self,
and_then: AndThen2,
) -> AndThenAsyncCaller<Out2, Out3, Self, R2, AndThen2>
where
Out3: for<'de> ArgumentDecoder<'de> + Send,
R2: Future<Output = Result<Out3, AgentError>> + Send,
AndThen2: Send + Fn(Out2) -> R2,
{
AndThenAsyncCaller::new(self, and_then)
}
pub fn map<Out3, Map2>(self, map: Map2) -> MappedAsyncCaller<Out2, Out3, Self, Map2>
where
Out3: for<'de> ArgumentDecoder<'de> + Send,
Map2: Send + Fn(Out2) -> Out3,
{
MappedAsyncCaller::new(self, map)
}
}
#[async_trait]
impl<Out, Out2, Inner, Map> AsyncCall<Out2> for MappedAsyncCaller<Out, Out2, Inner, Map>
where
Out: for<'de> ArgumentDecoder<'de> + Send,
Out2: for<'de> ArgumentDecoder<'de> + Send,
Inner: AsyncCall<Out> + Send,
Map: Send + Fn(Out) -> Out2,
{
async fn call(self) -> Result<RequestId, AgentError> {
self.call().await
}
async fn call_and_wait<W>(self, waiter: W) -> Result<Out2, AgentError>
where
W: Waiter,
{
self.call_and_wait(waiter).await
}
}