sylvia_derive/contract/communication/
executor.rs

1use convert_case::Case;
2use proc_macro2::TokenStream;
3use quote::quote;
4use syn::{GenericParam, Generics, Type};
5
6use crate::crate_module;
7use crate::parser::attributes::msg::MsgType;
8use crate::types::associated_types::EmitAssociated;
9use crate::types::msg_field::MsgField;
10use crate::types::msg_variant::{MsgVariant, MsgVariants};
11use crate::utils::SvCasing;
12
13/// Emits [execute helper](https://cosmwasm-docs.vercel.app/sylvia/macros/generated-types/communication#executor-helpers).
14///
15/// Generates trait containing methods for each execute message variant and implements it on
16/// `sylvia::types::ExecutorBuilder<EmptyExecutorBuilderState>`.
17pub struct Executor<'a> {
18    generics: Generics,
19    self_ty: Type,
20    variants: MsgVariants<'a, GenericParam>,
21}
22
23impl<'a> Executor<'a> {
24    pub fn new(generics: Generics, self_ty: Type, variants: MsgVariants<'a, GenericParam>) -> Self {
25        Self {
26            generics,
27            self_ty,
28            variants,
29        }
30    }
31
32    pub fn emit(&self) -> TokenStream {
33        let sylvia = crate_module();
34        let Self {
35            generics,
36            self_ty,
37            variants,
38            ..
39        } = self;
40
41        let where_clause = &generics.where_clause;
42        let generics: Vec<_> = generics.params.iter().collect();
43        let contract = &self_ty;
44
45        let accessor = MsgType::Exec.as_accessor_name();
46        let executor_api_path = quote! { < #contract as #sylvia ::types::ContractApi>:: #accessor };
47
48        let executor_methods_impl = variants
49            .variants()
50            .map(|variant| variant.emit_executor_impl(&executor_api_path));
51
52        let executor_methods_declaration = variants
53            .variants()
54            .map(|variant| variant.emit_executor_method_declaration());
55
56        let types_declaration = where_clause
57            .as_ref()
58            .map(EmitAssociated::emit_declaration)
59            .unwrap_or(vec![]);
60
61        let types_implementation = where_clause
62            .as_ref()
63            .map(EmitAssociated::emit_implementation)
64            .unwrap_or(vec![]);
65
66        quote! {
67            pub trait Executor<#(#generics,)*> #where_clause {
68                #(#types_declaration)*
69                #(#executor_methods_declaration)*
70            }
71
72            impl <#(#generics,)*> Executor<#(#generics,)*>
73                for #sylvia ::types::ExecutorBuilder<( #sylvia ::types::EmptyExecutorBuilderState, #contract )> #where_clause {
74                #(#types_implementation)*
75                #(#executor_methods_impl)*
76            }
77        }
78    }
79}
80
81trait EmitExecutorMethod {
82    fn emit_executor_impl(&self, api_path: &TokenStream) -> TokenStream;
83    fn emit_executor_method_declaration(&self) -> TokenStream;
84}
85
86impl EmitExecutorMethod for MsgVariant<'_> {
87    fn emit_executor_impl(&self, api_path: &TokenStream) -> TokenStream {
88        let name = self.name();
89        let fields = self.fields();
90        let sylvia = crate_module();
91
92        let parameters = fields.iter().map(MsgField::emit_method_field_folded);
93        let fields_names = fields.iter().map(MsgField::name);
94        let variant_name = name.to_case(Case::Snake);
95
96        quote! {
97            fn #variant_name(self, #(#parameters),*) -> Result<#sylvia ::types::ExecutorBuilder< #sylvia ::types::ReadyExecutorBuilderState >, #sylvia ::cw_std::StdError> {
98                Ok(#sylvia ::types::ExecutorBuilder::<#sylvia ::types::ReadyExecutorBuilderState>::new(
99                    self.contract().to_owned(),
100                    self.funds().to_owned(),
101                    #sylvia ::cw_std::to_json_binary( & #api_path :: #variant_name (#(#fields_names),*) )?,
102                ))
103            }
104        }
105    }
106
107    fn emit_executor_method_declaration(&self) -> TokenStream {
108        let name = self.name();
109        let sylvia = crate_module();
110
111        let parameters = self
112            .fields()
113            .iter()
114            .map(|field| field.emit_method_field_folded());
115        let variant_name = name.to_case(Case::Snake);
116
117        quote! {
118            fn #variant_name(self, #(#parameters),*) -> Result< #sylvia ::types::ExecutorBuilder<#sylvia ::types::ReadyExecutorBuilderState>, #sylvia ::cw_std::StdError>;
119        }
120    }
121}