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}