sylvia_derive/contract/communication/
enum_msg.rs

1use crate::crate_module;
2use crate::parser::attributes::MsgAttrForwarding;
3use crate::parser::variant_descs::AsVariantDescs;
4use crate::parser::{ContractErrorAttr, Custom, MsgType, ParsedSylviaAttributes};
5use crate::types::msg_variant::MsgVariants;
6use crate::utils::emit_bracketed_generics;
7use proc_macro2::TokenStream;
8use quote::quote;
9use syn::spanned::Spanned;
10use syn::{GenericParam, Ident, ItemImpl, Type, WhereClause};
11
12/// Representation of single enum message
13pub struct EnumMessage<'a> {
14    variants: MsgVariants<'a, GenericParam>,
15    msg_ty: MsgType,
16    contract: &'a Type,
17    error: &'a ContractErrorAttr,
18    custom: &'a Custom,
19    where_clause: &'a Option<WhereClause>,
20    msg_attrs_to_forward: Vec<MsgAttrForwarding>,
21}
22
23impl<'a> EnumMessage<'a> {
24    pub fn new(
25        source: &'a ItemImpl,
26        msg_ty: MsgType,
27        generics: &'a [&'a GenericParam],
28        error: &'a ContractErrorAttr,
29        custom: &'a Custom,
30    ) -> Self {
31        let where_clause = &source.generics.where_clause;
32        let variants = MsgVariants::new(source.as_variants(), msg_ty, generics, where_clause);
33        let msg_attrs_to_forward = ParsedSylviaAttributes::new(source.attrs.iter())
34            .msg_attrs_forward
35            .into_iter()
36            .filter(|attr| attr.msg_type == msg_ty)
37            .collect();
38
39        Self {
40            variants,
41            msg_ty,
42            contract: &source.self_ty,
43            error,
44            custom,
45            where_clause,
46            msg_attrs_to_forward,
47        }
48    }
49
50    pub fn emit(&self) -> TokenStream {
51        let sylvia = crate_module();
52
53        let Self {
54            variants,
55            msg_ty,
56            contract,
57            error,
58            custom,
59            where_clause,
60            msg_attrs_to_forward,
61            ..
62        } = self;
63
64        let enum_name = msg_ty.emit_msg_name();
65        let match_arms = variants.emit_dispatch_legs();
66        let unused_generics = variants.unused_generics();
67        let bracketed_unused_generics = emit_bracketed_generics(unused_generics);
68        let used_generics = variants.used_generics();
69        let bracketed_used_generics = emit_bracketed_generics(used_generics);
70
71        let mut variant_names = variants.as_names_snake_cased();
72        variant_names.sort();
73        let variants_cnt = variant_names.len();
74        let variants_constructors = variants.emit_constructors();
75        let variants = variants.emit();
76
77        let ctx_type = msg_ty.emit_ctx_type(&custom.query_or_default());
78        let ret_type = msg_ty.emit_result_type(&custom.msg_or_default(), &error.error);
79        let derive_call = msg_ty.emit_derive_call();
80        let ep_name = msg_ty.emit_ep_name();
81        let messages_fn_name = Ident::new(&format!("{}_messages", ep_name), contract.span());
82
83        let phantom_variant = msg_ty.emit_phantom_variant(used_generics);
84        let phantom_match_arm = match !used_generics.is_empty() {
85            true => quote! {
86                _Phantom(_) => Err(#sylvia ::cw_std::StdError::generic_err("Phantom message should not be constructed.")).map_err(Into::into),
87            },
88            false => quote! {},
89        };
90        let msg_attrs_to_forward = msg_attrs_to_forward.iter().map(|attr| &attr.attrs);
91
92        quote! {
93            #[allow(clippy::derive_partial_eq_without_eq)]
94            #derive_call
95            #( #[ #msg_attrs_to_forward ] )*
96            #[serde(rename_all="snake_case")]
97            pub enum #enum_name #bracketed_used_generics {
98                #(#variants,)*
99                #phantom_variant
100            }
101
102            impl #bracketed_used_generics #enum_name #bracketed_used_generics {
103                pub fn dispatch #bracketed_unused_generics (self, contract: &#contract, ctx: #ctx_type) -> #ret_type #where_clause {
104                    use #enum_name::*;
105
106                    match self {
107                        #(#match_arms,)*
108                        #phantom_match_arm
109                    }
110                }
111
112                #(#variants_constructors)*
113            }
114
115            pub const fn #messages_fn_name () -> [&'static str; #variants_cnt] {
116                [#(#variant_names,)*]
117            }
118        }
119    }
120}