1use crate::prelude::*;
2use core::{
3 any::TypeId,
4 error::Error,
5 marker::PhantomData,
6 pin::Pin,
7 task::{Context, Poll},
8};
9use futures::Stream;
10
11#[cfg(feature = "gtest")]
12#[cfg(not(target_arch = "wasm32"))]
13mod gtest_env;
14#[cfg(feature = "gtest")]
15#[cfg(not(target_arch = "wasm32"))]
16pub use gtest_env::{BlockRunMode, GtestEnv, GtestError, GtestParams};
17
18#[cfg(feature = "gclient")]
19#[cfg(not(target_arch = "wasm32"))]
20mod gclient_env;
21#[cfg(feature = "gclient")]
22#[cfg(not(target_arch = "wasm32"))]
23pub use gclient_env::{GclientEnv, GclientError, GclientParams};
24
25mod gstd_env;
26pub use gstd_env::{GstdEnv, GstdParams};
27
28pub(crate) const PENDING_CALL_INVALID_STATE: &str =
29 "PendingCall polled after completion or invalid state";
30pub(crate) const PENDING_CTOR_INVALID_STATE: &str =
31 "PendingCtor polled after completion or invalid state";
32
33pub trait GearEnv: Clone {
34 type Params: Default;
35 type Error: Error;
36 type MessageState;
37
38 fn deploy<P: Program>(&self, code_id: CodeId, salt: Vec<u8>) -> Deployment<P, Self> {
39 Deployment::new(self.clone(), code_id, salt)
40 }
41}
42
43pub trait Program: Sized {
44 fn deploy(code_id: CodeId, salt: Vec<u8>) -> Deployment<Self, GstdEnv> {
45 Deployment::new(GstdEnv, code_id, salt)
46 }
47
48 fn client(program_id: ActorId) -> Actor<Self, GstdEnv> {
49 Actor::new(GstdEnv, program_id)
50 }
51}
52
53pub type Route = &'static str;
54
55#[derive(Debug, Clone)]
56pub struct Deployment<A, E: GearEnv = GstdEnv> {
57 env: E,
58 code_id: CodeId,
59 salt: Vec<u8>,
60 _phantom: PhantomData<A>,
61}
62
63impl<A, E: GearEnv> Deployment<A, E> {
64 pub fn new(env: E, code_id: CodeId, salt: Vec<u8>) -> Self {
65 Deployment {
66 env,
67 code_id,
68 salt,
69 _phantom: PhantomData,
70 }
71 }
72
73 pub fn with_env<N: GearEnv>(self, env: &N) -> Deployment<A, N> {
74 let Self {
75 env: _,
76 code_id,
77 salt,
78 _phantom: _,
79 } = self;
80 Deployment {
81 env: env.clone(),
82 code_id,
83 salt,
84 _phantom: PhantomData,
85 }
86 }
87
88 pub fn pending_ctor<T: CallCodec>(self, args: T::Params) -> PendingCtor<A, T, E> {
89 PendingCtor::new(self.env, self.code_id, self.salt, args)
90 }
91}
92
93#[derive(Debug, Clone)]
94pub struct Actor<A, E: GearEnv = GstdEnv> {
95 env: E,
96 id: ActorId,
97 _phantom: PhantomData<A>,
98}
99
100impl<A, E: GearEnv> Actor<A, E> {
101 pub fn new(env: E, id: ActorId) -> Self {
102 Actor {
103 env,
104 id,
105 _phantom: PhantomData,
106 }
107 }
108
109 pub fn id(&self) -> ActorId {
110 self.id
111 }
112
113 pub fn with_env<N: GearEnv>(self, env: &N) -> Actor<A, N> {
114 let Self {
115 env: _,
116 id,
117 _phantom: _,
118 } = self;
119 Actor {
120 env: env.clone(),
121 id,
122 _phantom: PhantomData,
123 }
124 }
125
126 pub fn with_actor_id(mut self, actor_id: ActorId) -> Self {
127 self.id = actor_id;
128 self
129 }
130
131 pub fn service<S>(&self, route: Route) -> Service<S, E> {
132 Service::new(self.env.clone(), self.id, route)
133 }
134}
135
136#[derive(Debug, Clone)]
137pub struct Service<S, E: GearEnv = GstdEnv> {
138 env: E,
139 actor_id: ActorId,
140 route: Route,
141 _phantom: PhantomData<S>,
142}
143
144impl<S, E: GearEnv> Service<S, E> {
145 pub fn new(env: E, actor_id: ActorId, route: Route) -> Self {
146 Service {
147 env,
148 actor_id,
149 route,
150 _phantom: PhantomData,
151 }
152 }
153
154 pub fn actor_id(&self) -> ActorId {
155 self.actor_id
156 }
157
158 pub fn route(&self) -> Route {
159 self.route
160 }
161
162 pub fn with_actor_id(mut self, actor_id: ActorId) -> Self {
163 self.actor_id = actor_id;
164 self
165 }
166
167 pub fn pending_call<T: CallCodec>(&self, args: T::Params) -> PendingCall<T, E> {
168 PendingCall::new(self.env.clone(), self.actor_id, self.route, args)
169 }
170
171 #[cfg(not(target_arch = "wasm32"))]
172 pub fn listener(&self) -> ServiceListener<S::Event, E>
173 where
174 S: ServiceWithEvents,
175 {
176 ServiceListener::new(self.env.clone(), self.actor_id, self.route)
177 }
178}
179
180#[cfg(not(target_arch = "wasm32"))]
181pub trait ServiceWithEvents {
182 type Event: Event;
183}
184
185#[cfg(not(target_arch = "wasm32"))]
186pub struct ServiceListener<D: Event, E: GearEnv> {
187 env: E,
188 actor_id: ActorId,
189 route: Route,
190 _phantom: PhantomData<D>,
191}
192
193#[cfg(not(target_arch = "wasm32"))]
194impl<D: Event, E: GearEnv> ServiceListener<D, E> {
195 pub fn new(env: E, actor_id: ActorId, route: Route) -> Self {
196 ServiceListener {
197 env,
198 actor_id,
199 route,
200 _phantom: PhantomData,
201 }
202 }
203
204 pub async fn listen(
205 &self,
206 ) -> Result<impl Stream<Item = (ActorId, D)> + Unpin, <E as GearEnv>::Error>
207 where
208 E: Listener<Error = <E as GearEnv>::Error>,
209 {
210 let self_id = self.actor_id;
211 let prefix = self.route;
212 self.env
213 .listen(move |(actor_id, payload)| {
214 if actor_id != self_id {
215 return None;
216 }
217 D::decode_event(prefix, payload).ok().map(|e| (actor_id, e))
218 })
219 .await
220 }
221}
222
223pin_project_lite::pin_project! {
224 pub struct PendingCall<T: CallCodec, E: GearEnv> {
225 env: E,
226 destination: ActorId,
227 route: Route,
228 params: Option<E::Params>,
229 args: Option<T::Params>,
230 #[pin]
231 state: Option<E::MessageState>
232 }
233}
234
235impl<T: CallCodec, E: GearEnv> PendingCall<T, E> {
236 pub fn new(env: E, destination: ActorId, route: Route, args: T::Params) -> Self {
237 PendingCall {
238 env,
239 destination,
240 route,
241 params: None,
242 args: Some(args),
243 state: None,
244 }
245 }
246
247 pub fn with_destination(mut self, actor_id: ActorId) -> Self {
248 self.destination = actor_id;
249 self
250 }
251
252 pub fn with_params(mut self, f: impl FnOnce(E::Params) -> E::Params) -> Self {
253 self.params = Some(f(self.params.unwrap_or_default()));
254 self
255 }
256
257 #[inline]
258 fn take_encoded_args_and_params(&mut self) -> (Vec<u8>, E::Params) {
259 let args = self
260 .args
261 .take()
262 .unwrap_or_else(|| panic!("{PENDING_CALL_INVALID_STATE}"));
263 let payload = T::encode_params_with_prefix(self.route, &args);
264 let params = self.params.take().unwrap_or_default();
265 (payload, params)
266 }
267}
268
269pin_project_lite::pin_project! {
270 pub struct PendingCtor<A, T: CallCodec, E: GearEnv> {
271 env: E,
272 code_id: CodeId,
273 params: Option<E::Params>,
274 salt: Option<Vec<u8>>,
275 args: Option<T::Params>,
276 _actor: PhantomData<A>,
277 #[pin]
278 state: Option<E::MessageState>,
279 program_id: Option<ActorId>,
280 }
281}
282
283impl<A, T: CallCodec, E: GearEnv> PendingCtor<A, T, E> {
284 pub fn new(env: E, code_id: CodeId, salt: Vec<u8>, args: T::Params) -> Self {
285 PendingCtor {
286 env,
287 code_id,
288 params: None,
289 salt: Some(salt),
290 args: Some(args),
291 _actor: PhantomData,
292 state: None,
293 program_id: None,
294 }
295 }
296
297 pub fn with_params(mut self, f: impl FnOnce(E::Params) -> E::Params) -> Self {
298 self.params = Some(f(self.params.unwrap_or_default()));
299 self
300 }
301}
302
303pub trait CallCodec {
304 const ROUTE: Route;
305 type Params: Encode;
306 type Reply: Decode + 'static;
307
308 fn encode_params(value: &Self::Params) -> Vec<u8> {
309 let size = Encode::encoded_size(Self::ROUTE) + Encode::encoded_size(value);
310 let mut result = Vec::with_capacity(size);
311 Encode::encode_to(Self::ROUTE, &mut result);
312 Encode::encode_to(value, &mut result);
313 result
314 }
315
316 fn encode_params_with_prefix(prefix: Route, value: &Self::Params) -> Vec<u8> {
317 let size = Encode::encoded_size(prefix)
318 + Encode::encoded_size(Self::ROUTE)
319 + Encode::encoded_size(value);
320 let mut result = Vec::with_capacity(size);
321 Encode::encode_to(prefix, &mut result);
322 Encode::encode_to(Self::ROUTE, &mut result);
323 Encode::encode_to(value, &mut result);
324 result
325 }
326
327 fn decode_reply(payload: impl AsRef<[u8]>) -> Result<Self::Reply, parity_scale_codec::Error> {
328 let mut value = payload.as_ref();
329 if Self::is_empty_tuple::<Self::Reply>() {
330 return Decode::decode(&mut value);
331 }
332 let route = String::decode(&mut value)?;
334 if route != Self::ROUTE {
335 return Err("Invalid reply prefix".into());
336 }
337 Decode::decode(&mut value)
338 }
339
340 fn decode_reply_with_prefix(
341 prefix: Route,
342 payload: impl AsRef<[u8]>,
343 ) -> Result<Self::Reply, parity_scale_codec::Error> {
344 let mut value = payload.as_ref();
345 if Self::is_empty_tuple::<Self::Reply>() {
346 return Decode::decode(&mut value);
347 }
348 let route = String::decode(&mut value)?;
350 if route != prefix {
351 return Err("Invalid reply prefix".into());
352 }
353 let route = String::decode(&mut value)?;
354 if route != Self::ROUTE {
355 return Err("Invalid reply prefix".into());
356 }
357 Decode::decode(&mut value)
358 }
359
360 fn with_optimized_encode<R>(
361 prefix: Route,
362 value: &Self::Params,
363 f: impl FnOnce(&[u8]) -> R,
364 ) -> R {
365 let size = Encode::encoded_size(prefix)
366 + Encode::encoded_size(Self::ROUTE)
367 + Encode::encoded_size(value);
368 gcore::stack_buffer::with_byte_buffer(size, |buffer| {
369 let mut buffer_writer = crate::utils::MaybeUninitBufferWriter::new(buffer);
370 Encode::encode_to(prefix, &mut buffer_writer);
371 Encode::encode_to(Self::ROUTE, &mut buffer_writer);
372 Encode::encode_to(value, &mut buffer_writer);
373 buffer_writer.with_buffer(f)
374 })
375 }
376
377 fn is_empty_tuple<T: 'static>() -> bool {
378 TypeId::of::<T>() == TypeId::of::<()>()
379 }
380}
381
382#[macro_export]
383macro_rules! params_struct_impl {
384 (
385 $env:ident,
386 $name:ident { $( $(#[$attr:meta])* $vis:vis $field:ident: $ty:ty ),* $(,)? }
387 ) => {
388 #[derive(Debug, Default)]
389 pub struct $name {
390 $(
391 $(#[$attr])* $vis $field : Option< $ty >,
392 )*
393 }
394
395 impl $name {
396 $(
397 paste::paste! {
398 $(#[$attr])*
399 pub fn [<with_ $field>](mut self, $field: $ty) -> Self {
400 self.$field = Some($field);
401 self
402 }
403 }
404 )*
405 }
406
407 impl<A, T: CallCodec> PendingCtor<A, T, $env> {
408 $(
409 paste::paste! {
410 $(#[$attr])*
411 pub fn [<with_ $field>](self, $field: $ty) -> Self {
412 self.with_params(|params| params.[<with_ $field>]($field))
413 }
414 }
415 )*
416 }
417
418 impl<T: CallCodec> PendingCall<T, $env> {
419 $(
420 paste::paste! {
421 $(#[$attr])*
422 pub fn [<with_ $field>](self, $field: $ty) -> Self {
423 self.with_params(|params| params.[<with_ $field>]($field))
424 }
425 }
426 )*
427 }
428 };
429}
430
431#[macro_export]
432macro_rules! params_for_pending_impl {
433 (
434 $env:ident,
435 $name:ident { $( $(#[$attr:meta])* $vis:vis $field:ident: $ty:ty ),* $(,)? }
436 ) => {
437 impl $name {
438 $(
439 paste::paste! {
440 $(#[$attr])*
441 pub fn [<with_ $field>](mut self, $field: $ty) -> Self {
442 self.$field = Some($field);
443 self
444 }
445 }
446 )*
447 }
448
449 impl<A, T: CallCodec> PendingCtor<A, T, $env> {
450 $(
451 paste::paste! {
452 $(#[$attr])*
453 pub fn [<with_ $field>](self, $field: $ty) -> Self {
454 self.with_params(|params| params.[<with_ $field>]($field))
455 }
456 }
457 )*
458 }
459
460 impl<T: CallCodec> PendingCall<T, $env> {
461 $(
462 paste::paste! {
463 $(#[$attr])*
464 pub fn [<with_ $field>](self, $field: $ty) -> Self {
465 self.with_params(|params| params.[<with_ $field>]($field))
466 }
467 }
468 )*
469 }
470 };
471}
472
473#[macro_export]
474macro_rules! io_struct_impl {
475 (
476 $name:ident ( $( $param:ident : $ty:ty ),* ) -> $reply:ty
477 ) => {
478 pub struct $name(());
479 impl $name {
480 pub fn encode_params($( $param: $ty, )* ) -> Vec<u8> {
481 <$name as CallCodec>::encode_params(&( $( $param, )* ))
482 }
483 pub fn encode_params_with_prefix(prefix: Route, $( $param: $ty, )* ) -> Vec<u8> {
484 <$name as CallCodec>::encode_params_with_prefix(prefix, &( $( $param, )* ))
485 }
486 }
487 impl CallCodec for $name {
488 const ROUTE: &'static str = stringify!($name);
489 type Params = ( $( $ty, )* );
490 type Reply = $reply;
491 }
492 };
493}
494
495#[allow(unused_macros)]
496macro_rules! str_scale_encode {
497 ($s:ident) => {{
498 const S: &str = stringify!($s);
499 assert!(S.len() <= 63, "Ident too long for encoding");
500 const LEN: u8 = S.len() as u8;
501 const BYTES: [u8; LEN as usize + 1] = {
502 const fn to_array(s: &str) -> [u8; LEN as usize + 1] {
503 let bytes = s.as_bytes();
504 let mut out = [0u8; LEN as usize + 1];
505 out[0] = LEN << 2;
506 let mut i = 0;
507 while i < LEN as usize {
508 out[i + 1] = bytes[i];
509 i += 1;
510 }
511 out
512 }
513 to_array(S)
514 };
515 BYTES.as_slice()
516 }};
517}
518
519#[allow(async_fn_in_trait)]
520pub trait Listener {
521 type Error: Error;
522
523 async fn listen<E, F: FnMut((ActorId, Vec<u8>)) -> Option<(ActorId, E)>>(
524 &self,
525 f: F,
526 ) -> Result<impl Stream<Item = (ActorId, E)> + Unpin, Self::Error>;
527}
528
529#[cfg(not(target_arch = "wasm32"))]
530pub trait Event: Decode {
531 const EVENT_NAMES: &'static [Route];
532
533 fn decode_event(
534 prefix: Route,
535 payload: impl AsRef<[u8]>,
536 ) -> Result<Self, parity_scale_codec::Error> {
537 let mut payload = payload.as_ref();
538 let route = String::decode(&mut payload)?;
539 if route != prefix {
540 return Err("Invalid event prefix".into());
541 }
542 let evt_name = String::decode(&mut payload)?;
543 for (idx, &name) in Self::EVENT_NAMES.iter().enumerate() {
544 if evt_name == name {
545 let idx = idx as u8;
546 let bytes = [&[idx], payload].concat();
547 let mut event_bytes = &bytes[..];
548 return Decode::decode(&mut event_bytes);
549 }
550 }
551 Err("Invalid event name".into())
552 }
553}
554
555#[cfg(test)]
556mod tests {
557 use super::*;
558 io_struct_impl!(Add (value: u32) -> u32);
559 io_struct_impl!(Value () -> u32);
560
561 #[test]
562 fn test_str_encode() {
563 const ADD: &[u8] = str_scale_encode!(Add);
564 assert_eq!(ADD, &[12, 65, 100, 100]);
565
566 const VALUE: &[u8] = str_scale_encode!(Value);
567 assert_eq!(VALUE, &[20, 86, 97, 108, 117, 101]);
568 }
569
570 #[test]
571 fn test_io_struct_impl() {
572 let add = Add::encode_params(42);
573 assert_eq!(add, &[12, 65, 100, 100, 42, 0, 0, 0]);
574
575 let value = Add::encode_params_with_prefix("Counter", 42);
576 assert_eq!(
577 value,
578 &[
579 28, 67, 111, 117, 110, 116, 101, 114, 12, 65, 100, 100, 42, 0, 0, 0
580 ]
581 );
582
583 let value = Value::encode_params();
584 assert_eq!(value, &[20, 86, 97, 108, 117, 101]);
585
586 let value = Value::encode_params_with_prefix("Counter");
587 assert_eq!(
588 value,
589 &[
590 28, 67, 111, 117, 110, 116, 101, 114, 20, 86, 97, 108, 117, 101
591 ]
592 );
593 }
594}