sylvia_derive/interface/communication/
querier.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
14/// Emits [query helper](https://cosmwasm-docs.vercel.app/sylvia/macros/generated-types/communication#query-helpers).
15///
16/// Generates trait containing methods for each query message variant and implements it on
17/// `sylvia::types::BoundQuerier<Contract>`.
18pub struct Querier<'a, Generic> {
19    variants: &'a MsgVariants<'a, Generic>,
20    associated_types: &'a AssociatedTypes<'a>,
21    interface_name: &'a Ident,
22}
23
24impl<'a, Generic> Querier<'a, Generic>
25where
26    Generic: GetPath + PartialEq + ToTokens,
27{
28    pub fn new(
29        variants: &'a MsgVariants<'a, Generic>,
30        associated_types: &'a AssociatedTypes,
31        interface_name: &'a Ident,
32    ) -> Self {
33        Self {
34            variants,
35            associated_types,
36            interface_name,
37        }
38    }
39
40    pub fn emit_querier_trait(&self) -> TokenStream {
41        let sylvia = crate_module();
42        let Self {
43            variants,
44            associated_types,
45            interface_name,
46        } = self;
47
48        let generics: Vec<_> = associated_types
49            .without_error()
50            .map(ItemType::as_name)
51            .collect();
52        let all_generics: Vec<_> = associated_types.as_names().collect();
53        let accessor = MsgType::Query.as_accessor_name();
54        let api_path = quote! {
55            < dyn #interface_name < Error = (), #(#generics = Self:: #generics,)* > as InterfaceMessagesApi > :: #accessor
56        };
57
58        let methods_trait_impl = variants
59            .variants()
60            .map(|variant| variant.emit_querier_impl(&api_path))
61            .collect::<Vec<_>>();
62
63        let querier_methods_declaration = variants
64            .variants()
65            .map(|variant| variant.emit_querier_method_declaration());
66
67        let types_declaration = associated_types.without_error().collect::<Vec<_>>();
68        let where_clause = associated_types.as_where_clause();
69
70        quote! {
71            pub trait Querier {
72                #(#types_declaration)*
73                #(#querier_methods_declaration)*
74            }
75
76            impl <'a, C: #sylvia ::cw_std::CustomQuery, #(#all_generics,)*> Querier for #sylvia ::types::BoundQuerier<'a, C, dyn #interface_name <#( #all_generics = #all_generics,)*> > #where_clause {
77                #(type #generics = #generics;)*
78                #(#methods_trait_impl)*
79            }
80
81            impl <'a, C: #sylvia ::cw_std::CustomQuery, Contract: #interface_name> Querier for #sylvia ::types::BoundQuerier<'a, C, Contract> {
82                #(type #generics = <Contract as #interface_name > :: #generics;)*
83                #(#methods_trait_impl)*
84            }
85        }
86    }
87}
88
89trait EmitQuerierMethod {
90    fn emit_querier_impl(&self, api_path: &TokenStream) -> TokenStream;
91    fn emit_querier_method_declaration(&self) -> TokenStream;
92}
93
94impl EmitQuerierMethod for MsgVariant<'_> {
95    fn emit_querier_impl(&self, api_path: &TokenStream) -> TokenStream {
96        let sylvia = crate_module();
97        let name = self.name();
98        let fields = self.fields();
99        let return_type = self.return_type();
100
101        let parameters = fields.iter().map(MsgField::emit_method_field_folded);
102        let fields_names = fields.iter().map(MsgField::name);
103        let variant_name = name.to_case(Case::Snake);
104
105        quote! {
106            fn #variant_name(&self, #(#parameters),*) -> Result< #return_type, #sylvia:: cw_std::StdError> {
107                let query = #api_path :: #variant_name (#(#fields_names),*);
108                self.querier().query_wasm_smart(self.contract(), &query)
109            }
110        }
111    }
112
113    fn emit_querier_method_declaration(&self) -> TokenStream {
114        let sylvia = crate_module();
115        let name = self.name();
116        let return_type = self.return_type();
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< #return_type, #sylvia:: cw_std::StdError>;
126        }
127    }
128}