sylvia_derive/
contract.rs

1use communication::api::Api;
2use communication::enum_msg::EnumMessage;
3use communication::executor::Executor;
4use communication::instantiate_builder::InstantiateBuilder;
5use communication::querier::Querier;
6use communication::reply::Reply;
7use communication::struct_msg::StructMessage;
8use communication::wrapper_msg::GlueMessage;
9use mt::MtHelpers;
10use proc_macro2::TokenStream;
11use quote::quote;
12use syn::{GenericParam, ItemImpl};
13
14use crate::parser::attributes::features::SylviaFeatures;
15use crate::parser::attributes::msg::MsgType;
16use crate::parser::variant_descs::AsVariantDescs;
17use crate::parser::{
18    assert_new_method_defined, ContractErrorAttr, Custom, OverrideEntryPoint,
19    ParsedSylviaAttributes,
20};
21use crate::types::interfaces::Interfaces;
22use crate::types::msg_variant::MsgVariants;
23
24mod communication;
25mod mt;
26
27/// Preprocessed `contract` macro input for struct impl block.
28///
29/// Generates:
30///     - [Messages](https://cosmwasm-docs.vercel.app/sylvia/macros/generated-types/message-types#contract-messages)
31///         - InstantiateMsg
32///         - ExecMsg
33///         - QueryMsg
34///         - SudoMsg
35///         - MigrateMsg
36///         - ContractExecMsg
37///         - ContractQueryMsg
38///         - ContractSudoMsg
39///     - [MultiTest](https://cosmwasm-docs.vercel.app/sylvia/macros/generated-types/multitest) helpers
40///     - [Querier](https://cosmwasm-docs.vercel.app/cw-multi-test) trait implementation
41///     - [Executor](https://cosmwasm-docs.vercel.app/cw-multi-test) trait implementation
42///     - Api trait implementation
43pub struct ContractInput<'a> {
44    item: &'a ItemImpl,
45    generics: Vec<&'a GenericParam>,
46    error: ContractErrorAttr,
47    custom: Custom,
48    override_entry_points: Vec<OverrideEntryPoint>,
49    interfaces: Interfaces,
50    sv_features: SylviaFeatures,
51}
52
53impl<'a> ContractInput<'a> {
54    pub fn new(item: &'a ItemImpl) -> Self {
55        assert_new_method_defined(item);
56
57        let generics = item.generics.params.iter().collect();
58        let parsed_attrs = ParsedSylviaAttributes::new(item.attrs.iter());
59        let error = parsed_attrs.error_attrs.unwrap_or_default();
60        let custom = parsed_attrs.custom_attr.unwrap_or_default();
61        let sv_features = parsed_attrs.sv_features;
62        let override_entry_points = parsed_attrs.override_entry_point_attrs;
63        let interfaces = Interfaces::new(item);
64
65        Self {
66            item,
67            generics,
68            error,
69            custom,
70            override_entry_points,
71            interfaces,
72            sv_features,
73        }
74    }
75
76    /// Processes the input and generates the contract code.
77    pub fn process(&self) -> TokenStream {
78        let Self {
79            item,
80            generics,
81            custom,
82            ..
83        } = self;
84        let multitest_helpers = self.emit_multitest_helpers();
85        let messages = self.emit_messages();
86        let contract_api = Api::new(item, generics, custom).emit();
87        let querier = self.emit_querier();
88        let executor = self.emit_executor();
89        let reply = self.emit_reply();
90        let instantiate_builder = self.emit_instantiate_builder_trait();
91
92        quote! {
93            pub mod sv {
94                use super::*;
95
96                #messages
97
98                #multitest_helpers
99
100                #querier
101
102                #executor
103
104                #reply
105
106                #contract_api
107
108                #instantiate_builder
109            }
110        }
111    }
112
113    fn emit_messages(&self) -> TokenStream {
114        let instantiate = self.emit_struct_msg(MsgType::Instantiate);
115        let migrate = self.emit_struct_msg(MsgType::Migrate);
116        let exec_impl = self.emit_enum_msg(MsgType::Exec);
117        let query_impl = self.emit_enum_msg(MsgType::Query);
118        let sudo_impl = self.emit_enum_msg(MsgType::Sudo);
119        let exec = self.emit_glue_msg(MsgType::Exec);
120        let query = self.emit_glue_msg(MsgType::Query);
121        let sudo = self.emit_glue_msg(MsgType::Sudo);
122
123        quote! {
124            #instantiate
125
126            #exec_impl
127
128            #query_impl
129
130            #sudo_impl
131
132            #migrate
133
134            #exec
135
136            #query
137
138            #sudo
139        }
140    }
141
142    fn emit_struct_msg(&self, msg_ty: MsgType) -> TokenStream {
143        StructMessage::new(self.item, msg_ty, &self.generics, &self.error, &self.custom)
144            .map_or(quote! {}, |msg| msg.emit())
145    }
146
147    fn emit_enum_msg(&self, msg_ty: MsgType) -> TokenStream {
148        EnumMessage::new(self.item, msg_ty, &self.generics, &self.error, &self.custom).emit()
149    }
150
151    fn emit_glue_msg(&self, msg_ty: MsgType) -> TokenStream {
152        GlueMessage::new(
153            self.item,
154            msg_ty,
155            &self.error,
156            &self.custom,
157            &self.interfaces,
158        )
159        .emit()
160    }
161
162    fn emit_multitest_helpers(&self) -> TokenStream {
163        if !cfg!(feature = "mt") {
164            return quote! {};
165        }
166
167        let Self {
168            item,
169            custom,
170            override_entry_points,
171            ..
172        } = self;
173
174        let generic_params = &self.generics;
175        MtHelpers::new(item, generic_params, custom, override_entry_points.clone()).emit()
176    }
177
178    fn emit_executor(&self) -> TokenStream {
179        let item = self.item;
180        let variants = MsgVariants::new(item.as_variants(), MsgType::Exec, &[], &None);
181
182        Executor::new(item.generics.clone(), *item.self_ty.clone(), variants).emit()
183    }
184    fn emit_querier(&self) -> TokenStream {
185        let item = self.item;
186        let variants = MsgVariants::new(item.as_variants(), MsgType::Query, &[], &None);
187
188        Querier::new(item.generics.clone(), *item.self_ty.clone(), variants).emit()
189    }
190
191    fn emit_reply(&self) -> TokenStream {
192        if !self.sv_features.replies {
193            return quote! {};
194        }
195
196        let variants = MsgVariants::new(self.item.as_variants(), MsgType::Reply, &[], &None);
197
198        Reply::new(self.item, &self.generics, &variants).emit()
199    }
200
201    fn emit_instantiate_builder_trait(&self) -> TokenStream {
202        let variants = MsgVariants::new(
203            self.item.as_variants(),
204            MsgType::Instantiate,
205            &self.generics,
206            &self.item.generics.where_clause,
207        );
208        let where_clause = variants.where_clause();
209
210        match variants.get_only_variant() {
211            Some(variant) => InstantiateBuilder::new(
212                *self.item.self_ty.clone(),
213                variants.used_generics(),
214                &where_clause,
215                variant,
216            )
217            .emit(),
218            None => quote! {},
219        }
220    }
221}