sylvia_derive/contract/communication/
querier.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 [query helper](https://cosmwasm-docs.vercel.app/sylvia/macros/generated-types/communication#query-helpers).
14///
15/// Generates trait containing methods for each query message variant and implements it on
16/// `sylvia::types::BoundQuerier<Contract>`.
17pub struct Querier<'a> {
18    generics: Generics,
19    self_ty: Type,
20    variants: MsgVariants<'a, GenericParam>,
21}
22
23impl<'a> Querier<'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::Query.as_accessor_name();
46        let api_path = quote! { < #contract as #sylvia ::types::ContractApi>:: #accessor };
47
48        let querier_methods_impl = variants
49            .variants()
50            .map(|variant| variant.emit_querier_impl(&api_path));
51
52        let querier_methods_declaration = variants
53            .variants()
54            .map(|variant| variant.emit_querier_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        let bracketed_generics = if !generics.is_empty() {
67            quote! { < #(#generics,)* > }
68        } else {
69            quote! {}
70        };
71
72        quote! {
73            pub trait Querier #bracketed_generics {
74                #(#types_declaration)*
75                #(#querier_methods_declaration)*
76            }
77
78            impl <'sv_querier_lifetime, #(#generics,)* C: #sylvia ::cw_std::CustomQuery> Querier #bracketed_generics for #sylvia ::types::BoundQuerier<'sv_querier_lifetime, C, #contract > #where_clause {
79                #(#types_implementation)*
80                #(#querier_methods_impl)*
81            }
82        }
83    }
84}
85
86trait EmitQuerierMethod {
87    fn emit_querier_impl(&self, api_path: &TokenStream) -> TokenStream;
88    fn emit_querier_method_declaration(&self) -> TokenStream;
89}
90
91impl EmitQuerierMethod for MsgVariant<'_> {
92    fn emit_querier_impl(&self, api_path: &TokenStream) -> TokenStream {
93        let sylvia = crate_module();
94        let name = self.name();
95        let fields = self.fields();
96        let return_type = self.return_type();
97
98        let parameters = fields.iter().map(MsgField::emit_method_field_folded);
99        let fields_names = fields.iter().map(MsgField::name);
100        let variant_name = name.to_case(Case::Snake);
101
102        quote! {
103            fn #variant_name(&self, #(#parameters),*) -> Result< #return_type, #sylvia:: cw_std::StdError> {
104                let query = #api_path :: #variant_name (#(#fields_names),*);
105                self.querier().query_wasm_smart(self.contract(), &query)
106            }
107        }
108    }
109
110    fn emit_querier_method_declaration(&self) -> TokenStream {
111        let sylvia = crate_module();
112        let name = self.name();
113        let return_type = self.return_type();
114
115        let parameters = self
116            .fields()
117            .iter()
118            .map(|field| field.emit_method_field_folded());
119        let variant_name = name.to_case(Case::Snake);
120
121        quote! {
122            fn #variant_name(&self, #(#parameters),*) -> Result< #return_type, #sylvia:: cw_std::StdError>;
123        }
124    }
125}