sylvia_derive/interface/communication/
enum_msg.rs

1use crate::parser::attributes::MsgAttrForwarding;
2use crate::parser::{Custom, MsgType, ParsedSylviaAttributes};
3use crate::types::associated_types::{AssociatedTypes, ItemType, EXEC_TYPE, QUERY_TYPE};
4use crate::types::msg_variant::MsgVariants;
5use crate::utils::emit_bracketed_generics;
6use proc_macro2::TokenStream;
7use quote::quote;
8use syn::{parse_quote, Ident, ItemTrait, Type};
9
10/// Representation of single enum message
11pub struct EnumMessage<'a> {
12    source: &'a ItemTrait,
13    variants: MsgVariants<'a, Ident>,
14    associated_types: &'a AssociatedTypes<'a>,
15    msg_ty: MsgType,
16    resp_type: Type,
17    query_type: Type,
18    msg_attrs_to_forward: Vec<MsgAttrForwarding>,
19}
20
21impl<'a> EnumMessage<'a> {
22    pub fn new(
23        source: &'a ItemTrait,
24        msg_ty: MsgType,
25        custom: &'a Custom,
26        variants: MsgVariants<'a, Ident>,
27        associated_types: &'a AssociatedTypes<'a>,
28    ) -> Self {
29        let trait_name = &source.ident;
30        let associated_exec =
31            associated_types.emit_contract_custom_type_accessor(trait_name, EXEC_TYPE);
32        let associated_query =
33            associated_types.emit_contract_custom_type_accessor(trait_name, QUERY_TYPE);
34
35        let resp_type = custom
36            .msg
37            .clone()
38            .or(associated_exec)
39            .unwrap_or_else(Custom::default_type);
40
41        let query_type = custom
42            .query
43            .clone()
44            .or(associated_query)
45            .unwrap_or_else(Custom::default_type);
46
47        let msg_attrs_to_forward = ParsedSylviaAttributes::new(source.attrs.iter())
48            .msg_attrs_forward
49            .into_iter()
50            .filter(|attr| attr.msg_type == msg_ty)
51            .collect();
52
53        Self {
54            source,
55            variants,
56            associated_types,
57            msg_ty,
58            resp_type,
59            query_type,
60            msg_attrs_to_forward,
61        }
62    }
63
64    pub fn emit(&self) -> TokenStream {
65        let Self {
66            source,
67            variants,
68            associated_types,
69            msg_ty,
70            resp_type,
71            query_type,
72            msg_attrs_to_forward,
73        } = self;
74
75        let trait_name = &source.ident;
76        let enum_name = msg_ty.emit_msg_name();
77        let unique_enum_name =
78            Ident::new(&format!("{}{}", trait_name, enum_name), enum_name.span());
79
80        let match_arms = variants.emit_dispatch_legs();
81        let mut msgs = variants.as_names_snake_cased();
82        msgs.sort();
83        let msgs_cnt = msgs.len();
84        let variants_constructors = variants.emit_constructors();
85        let msg_variants = variants.emit();
86
87        let ctx_type = msg_ty.emit_ctx_type(query_type);
88        let dispatch_type = msg_ty.emit_result_type(resp_type, &parse_quote!(ContractT::Error));
89
90        let used_generics = variants.used_generics();
91        let unused_generics = variants.unused_generics();
92        let where_predicates = associated_types
93            .without_error()
94            .map(ItemType::as_where_predicate);
95        let where_clause = variants.where_clause();
96        let contract_predicate = associated_types.emit_contract_predicate(trait_name);
97
98        let phantom_variant = variants.emit_phantom_variant();
99        let phatom_match_arm = variants.emit_phantom_match_arm();
100        let bracketed_used_generics = emit_bracketed_generics(used_generics);
101
102        let ep_name = msg_ty.emit_ep_name();
103        let messages_fn_name = Ident::new(&format!("{}_messages", ep_name), enum_name.span());
104        let derive_call = msg_ty.emit_derive_call();
105        let msg_attrs_to_forward = msg_attrs_to_forward.iter().map(|attr| &attr.attrs);
106
107        quote! {
108            #[allow(clippy::derive_partial_eq_without_eq)]
109            #derive_call
110            #( #[ #msg_attrs_to_forward ] )*
111            #[serde(rename_all="snake_case")]
112            pub enum #unique_enum_name #bracketed_used_generics {
113                #(#msg_variants,)*
114                #phantom_variant
115            }
116            pub type #enum_name #bracketed_used_generics = #unique_enum_name #bracketed_used_generics;
117
118            impl #bracketed_used_generics #unique_enum_name #bracketed_used_generics #where_clause {
119                pub fn dispatch<ContractT, #(#unused_generics,)*>(self, contract: &ContractT, ctx: #ctx_type)
120                    -> #dispatch_type
121                where
122                    #(#where_predicates,)*
123                    #contract_predicate
124                {
125                    use #unique_enum_name::*;
126
127                    match self {
128                        #(#match_arms,)*
129                        #phatom_match_arm
130                    }
131                }
132                #(#variants_constructors)*
133            }
134
135            pub const fn #messages_fn_name () -> [&'static str; #msgs_cnt] {
136                [#(#msgs,)*]
137            }
138        }
139    }
140}