sails_rs/
calls.rs

1use crate::{
2    errors::{Error, Result, RtlError},
3    prelude::{any::TypeId, *},
4};
5use core::{future::Future, marker::PhantomData};
6
7pub trait Action {
8    type Args;
9
10    #[cfg(not(feature = "ethexe"))]
11    fn with_gas_limit(self, gas_limit: GasUnit) -> Self;
12    fn with_value(self, value: ValueUnit) -> Self;
13    fn with_args<F: FnOnce(Self::Args) -> Self::Args>(self, args_fn: F) -> Self;
14
15    #[cfg(not(feature = "ethexe"))]
16    fn gas_limit(&self) -> Option<GasUnit>;
17    fn value(&self) -> ValueUnit;
18    fn args(&self) -> &Self::Args;
19}
20
21#[allow(async_fn_in_trait)]
22pub trait Call: Action {
23    type Output;
24
25    async fn send(self, target: ActorId) -> Result<impl Reply<Output = Self::Output>>;
26
27    async fn send_recv(self, target: ActorId) -> Result<Self::Output>
28    where
29        Self: Sized,
30    {
31        self.send(target).await?.recv().await
32    }
33}
34
35#[allow(async_fn_in_trait)]
36pub trait Activation: Action {
37    async fn send<S: AsRef<[u8]>>(
38        self,
39        code_id: CodeId,
40        salt: S,
41    ) -> Result<impl Reply<Output = ActorId>>;
42
43    async fn send_recv<S: AsRef<[u8]>>(self, code_id: CodeId, salt: S) -> Result<ActorId>
44    where
45        Self: Sized,
46    {
47        self.send(code_id, salt).await?.recv().await
48    }
49}
50
51#[allow(async_fn_in_trait)]
52pub trait Query: Action {
53    type Output;
54
55    async fn recv(self, target: ActorId) -> Result<Self::Output>;
56}
57
58#[allow(async_fn_in_trait)]
59pub trait Reply {
60    type Output;
61
62    async fn recv(self) -> Result<Self::Output>;
63}
64
65struct CallTicket<TReplyFuture, TActionIo> {
66    reply_future: TReplyFuture,
67    _io: PhantomData<TActionIo>,
68}
69
70impl<TReplyFuture, TActionIo> CallTicket<TReplyFuture, TActionIo> {
71    pub(crate) fn new(reply_future: TReplyFuture) -> Self {
72        Self {
73            reply_future,
74            _io: PhantomData,
75        }
76    }
77}
78
79impl<TReplyFuture, TActionIo> Reply for CallTicket<TReplyFuture, TActionIo>
80where
81    TReplyFuture: Future<Output = Result<Vec<u8>>>,
82    TActionIo: ActionIo,
83{
84    type Output = TActionIo::Reply;
85
86    async fn recv(self) -> Result<Self::Output> {
87        let reply_bytes = self.reply_future.await?;
88        TActionIo::decode_reply(reply_bytes)
89    }
90}
91
92struct ActivationTicket<TReplyFuture, TActionIo> {
93    reply_future: TReplyFuture,
94    _io: PhantomData<TActionIo>,
95}
96
97impl<TReplyFuture, TActionIo> ActivationTicket<TReplyFuture, TActionIo> {
98    pub(crate) fn new(reply_future: TReplyFuture) -> Self {
99        Self {
100            reply_future,
101            _io: PhantomData,
102        }
103    }
104}
105
106impl<TReplyFuture, TActionIo> Reply for ActivationTicket<TReplyFuture, TActionIo>
107where
108    TReplyFuture: Future<Output = Result<(ActorId, Vec<u8>)>>,
109    TActionIo: ActionIo<Reply = ()>,
110{
111    type Output = ActorId;
112
113    async fn recv(self) -> Result<Self::Output> {
114        let (actor_id, payload) = self.reply_future.await?;
115        TActionIo::decode_reply(payload)?;
116        Ok(actor_id)
117    }
118}
119
120#[allow(async_fn_in_trait)]
121pub trait Remoting {
122    type Args: Default;
123
124    async fn activate(
125        self,
126        code_id: CodeId,
127        salt: impl AsRef<[u8]>,
128        payload: impl AsRef<[u8]>,
129        #[cfg(not(feature = "ethexe"))] gas_limit: Option<GasUnit>,
130        value: ValueUnit,
131        args: Self::Args,
132    ) -> Result<impl Future<Output = Result<(ActorId, Vec<u8>)>>>;
133
134    async fn message(
135        self,
136        target: ActorId,
137        payload: impl AsRef<[u8]>,
138        #[cfg(not(feature = "ethexe"))] gas_limit: Option<GasUnit>,
139        value: ValueUnit,
140        args: Self::Args,
141    ) -> Result<impl Future<Output = Result<Vec<u8>>>>;
142
143    async fn query(
144        self,
145        target: ActorId,
146        payload: impl AsRef<[u8]>,
147        #[cfg(not(feature = "ethexe"))] gas_limit: Option<GasUnit>,
148        value: ValueUnit,
149        args: Self::Args,
150    ) -> Result<Vec<u8>>;
151}
152
153pub struct RemotingAction<TRemoting: Remoting, TActionIo: ActionIo> {
154    remoting: TRemoting,
155    params: TActionIo::Params,
156    #[cfg(not(feature = "ethexe"))]
157    gas_limit: Option<GasUnit>,
158    value: ValueUnit,
159    args: TRemoting::Args,
160}
161
162impl<TRemoting: Remoting, TActionIo: ActionIo> RemotingAction<TRemoting, TActionIo> {
163    pub fn new(remoting: TRemoting, params: TActionIo::Params) -> Self {
164        Self {
165            remoting,
166            params,
167            #[cfg(not(feature = "ethexe"))]
168            gas_limit: Default::default(),
169            value: Default::default(),
170            args: Default::default(),
171        }
172    }
173}
174
175impl<TRemoting: Remoting, TActionIo: ActionIo> Action for RemotingAction<TRemoting, TActionIo> {
176    type Args = TRemoting::Args;
177
178    #[cfg(not(feature = "ethexe"))]
179    fn with_gas_limit(self, gas_limit: GasUnit) -> Self {
180        Self {
181            gas_limit: Some(gas_limit),
182            ..self
183        }
184    }
185
186    fn with_value(self, value: ValueUnit) -> Self {
187        Self { value, ..self }
188    }
189
190    fn with_args<F: FnOnce(Self::Args) -> Self::Args>(self, args_fn: F) -> Self {
191        let RemotingAction { args, .. } = self;
192        let args = args_fn(args);
193        Self { args, ..self }
194    }
195
196    #[cfg(not(feature = "ethexe"))]
197    fn gas_limit(&self) -> Option<GasUnit> {
198        self.gas_limit
199    }
200
201    fn value(&self) -> ValueUnit {
202        self.value
203    }
204
205    fn args(&self) -> &Self::Args {
206        &self.args
207    }
208}
209
210impl<TRemoting, TActionIo> Call for RemotingAction<TRemoting, TActionIo>
211where
212    TRemoting: Remoting,
213    TActionIo: ActionIo,
214{
215    type Output = TActionIo::Reply;
216
217    async fn send(self, target: ActorId) -> Result<impl Reply<Output = TActionIo::Reply>> {
218        let payload = TActionIo::encode_call(&self.params);
219        let reply_future = self
220            .remoting
221            .message(
222                target,
223                payload,
224                #[cfg(not(feature = "ethexe"))]
225                self.gas_limit,
226                self.value,
227                self.args,
228            )
229            .await?;
230        Ok(CallTicket::<_, TActionIo>::new(reply_future))
231    }
232}
233
234impl<TRemoting, TActionIo> Activation for RemotingAction<TRemoting, TActionIo>
235where
236    TRemoting: Remoting,
237    TActionIo: ActionIo<Reply = ()>,
238{
239    async fn send<S: AsRef<[u8]>>(
240        self,
241        code_id: CodeId,
242        salt: S,
243    ) -> Result<impl Reply<Output = ActorId>> {
244        let payload = TActionIo::encode_call(&self.params);
245        let reply_future = self
246            .remoting
247            .activate(
248                code_id,
249                salt,
250                payload,
251                #[cfg(not(feature = "ethexe"))]
252                self.gas_limit,
253                self.value,
254                self.args,
255            )
256            .await?;
257        Ok(ActivationTicket::<_, TActionIo>::new(reply_future))
258    }
259}
260
261impl<TRemoting, TActionIo> Query for RemotingAction<TRemoting, TActionIo>
262where
263    TRemoting: Remoting,
264    TActionIo: ActionIo,
265{
266    type Output = TActionIo::Reply;
267
268    async fn recv(self, target: ActorId) -> Result<Self::Output> {
269        let payload = TActionIo::encode_call(&self.params);
270        let reply_bytes = self
271            .remoting
272            .query(
273                target,
274                payload,
275                #[cfg(not(feature = "ethexe"))]
276                self.gas_limit,
277                self.value,
278                self.args,
279            )
280            .await?;
281        TActionIo::decode_reply(reply_bytes)
282    }
283}
284
285pub trait ActionIo {
286    const ROUTE: &'static [u8];
287    type Params: Encode;
288    type Reply: Decode + 'static;
289
290    fn encode_call(value: &Self::Params) -> Vec<u8> {
291        let mut result = Vec::with_capacity(Self::ROUTE.len() + Encode::size_hint(value));
292        result.extend_from_slice(Self::ROUTE);
293        Encode::encode_to(value, &mut result);
294        result
295    }
296
297    fn decode_reply(payload: impl AsRef<[u8]>) -> Result<Self::Reply> {
298        let mut value = payload.as_ref();
299        let zero_size_reply = Self::is_empty_tuple::<Self::Reply>();
300        if !zero_size_reply && !value.starts_with(Self::ROUTE) {
301            return Err(Error::Rtl(RtlError::ReplyPrefixMismatches));
302        }
303        let start_idx = if zero_size_reply {
304            0
305        } else {
306            Self::ROUTE.len()
307        };
308        value = &value[start_idx..];
309        Decode::decode(&mut value).map_err(Error::Codec)
310    }
311
312    fn is_empty_tuple<T: 'static>() -> bool {
313        TypeId::of::<T>() == TypeId::of::<()>()
314    }
315}