sylvia_derive/interface/communication/
executor.rs

1use convert_case::Case;
2use proc_macro2::TokenStream;
3use quote::{quote, ToTokens};
4use syn::Ident;
5
6use crate::crate_module;
7use crate::parser::attributes::msg::MsgType;
8use crate::parser::check_generics::GetPath;
9use crate::types::associated_types::{AssociatedTypes, ItemType};
10use crate::types::msg_field::MsgField;
11use crate::types::msg_variant::{MsgVariant, MsgVariants};
12use crate::utils::SvCasing;
13
14pub struct Executor<'a, Generic> {
15    variants: &'a MsgVariants<'a, Generic>,
16    associated_types: &'a AssociatedTypes<'a>,
17    interface_name: &'a Ident,
18}
19
20impl<'a, Generic> Executor<'a, Generic>
21where
22    Generic: GetPath + PartialEq + ToTokens,
23{
24    pub fn new(
25        variants: &'a MsgVariants<'a, Generic>,
26        associated_types: &'a AssociatedTypes,
27        interface_name: &'a Ident,
28    ) -> Self {
29        Self {
30            variants,
31            associated_types,
32            interface_name,
33        }
34    }
35
36    pub fn emit_executor_trait(&self) -> TokenStream {
37        let sylvia = crate_module();
38        let Self {
39            variants,
40            associated_types,
41            interface_name,
42        } = self;
43
44        let generics: Vec<_> = associated_types
45            .without_error()
46            .map(ItemType::as_name)
47            .collect();
48        let all_generics: Vec<_> = associated_types.as_names().collect();
49
50        let accessor = MsgType::Exec.as_accessor_name();
51        let executor_api_path = quote! {
52            < dyn #interface_name < Error = (), #(#generics = Self:: #generics,)* > as InterfaceMessagesApi >:: #accessor
53        };
54
55        let methods_trait_impl = variants
56            .variants()
57            .map(|variant| variant.emit_executor_impl(&executor_api_path))
58            .collect::<Vec<_>>();
59
60        let executor_methods_declaration = variants
61            .variants()
62            .map(|variant| variant.emit_executor_method_declaration());
63
64        let types_declaration = associated_types.without_error().collect::<Vec<_>>();
65        let where_clause = associated_types.as_where_clause();
66
67        quote! {
68            pub trait Executor {
69                #(#types_declaration)*
70                #(#executor_methods_declaration)*
71            }
72
73            impl <#(#all_generics,)*> Executor
74                for #sylvia ::types::ExecutorBuilder<(#sylvia ::types::EmptyExecutorBuilderState, dyn #interface_name <#( #all_generics = #all_generics,)* > ) > #where_clause {
75                #(type #generics = #generics;)*
76                #(#methods_trait_impl)*
77            }
78
79            impl <Contract: #interface_name> Executor
80                for #sylvia ::types::ExecutorBuilder<( #sylvia ::types::EmptyExecutorBuilderState, Contract )> {
81                #(type #generics = <Contract as #interface_name > :: #generics;)*
82                #(#methods_trait_impl)*
83            }
84        }
85    }
86}
87
88trait EmitExecutorMethod {
89    fn emit_executor_impl(&self, api_path: &TokenStream) -> TokenStream;
90    fn emit_executor_method_declaration(&self) -> TokenStream;
91}
92
93impl EmitExecutorMethod for MsgVariant<'_> {
94    fn emit_executor_impl(&self, api_path: &TokenStream) -> TokenStream {
95        let name = self.name();
96        let fields = self.fields();
97        let sylvia = crate_module();
98
99        let parameters = fields.iter().map(MsgField::emit_method_field_folded);
100        let fields_names = fields.iter().map(MsgField::name);
101        let variant_name = name.to_case(Case::Snake);
102
103        quote! {
104            fn #variant_name(self, #(#parameters),*) -> Result<#sylvia ::types::ExecutorBuilder< #sylvia ::types::ReadyExecutorBuilderState >, #sylvia ::cw_std::StdError> {
105                Ok(#sylvia ::types::ExecutorBuilder::<#sylvia ::types::ReadyExecutorBuilderState>::new(
106                    self.contract().to_owned(),
107                    self.funds().to_owned(),
108                    #sylvia ::cw_std::to_json_binary( & #api_path :: #variant_name (#(#fields_names),*) )?,
109                ))
110            }
111        }
112    }
113
114    fn emit_executor_method_declaration(&self) -> TokenStream {
115        let name = self.name();
116        let sylvia = crate_module();
117
118        let parameters = self
119            .fields()
120            .iter()
121            .map(|field| field.emit_method_field_folded());
122        let variant_name = name.to_case(Case::Snake);
123
124        quote! {
125            fn #variant_name(self, #(#parameters),*) -> Result< #sylvia ::types::ExecutorBuilder<#sylvia ::types::ReadyExecutorBuilderState>, #sylvia ::cw_std::StdError>;
126        }
127    }
128}