sylvia_derive/contract/communication/
wrapper_msg.rs1use crate::crate_module;
2use crate::fold::StripGenerics;
3use crate::parser::{ContractErrorAttr, Custom, MsgType};
4use crate::types::interfaces::Interfaces;
5use crate::utils::emit_bracketed_generics;
6use proc_macro2::TokenStream;
7use quote::quote;
8use syn::fold::Fold;
9use syn::spanned::Spanned;
10use syn::{Ident, ItemImpl, Type};
11
12#[derive(Debug)]
18pub struct GlueMessage<'a> {
19 source: &'a ItemImpl,
20 contract: &'a Type,
21 msg_ty: MsgType,
22 error: &'a ContractErrorAttr,
23 custom: &'a Custom,
24 interfaces: &'a Interfaces,
25}
26
27impl<'a> GlueMessage<'a> {
28 pub fn new(
29 source: &'a ItemImpl,
30 msg_ty: MsgType,
31 error: &'a ContractErrorAttr,
32 custom: &'a Custom,
33 interfaces: &'a Interfaces,
34 ) -> Self {
35 GlueMessage {
36 source,
37 contract: &source.self_ty,
38 msg_ty,
39 error,
40 custom,
41 interfaces,
42 }
43 }
44
45 pub fn emit(&self) -> TokenStream {
46 let sylvia = crate_module();
47 let Self {
48 source,
49 contract,
50 msg_ty,
51 error,
52 custom,
53 interfaces,
54 ..
55 } = self;
56
57 let generics: Vec<_> = source.generics.params.iter().collect();
58 let full_where_clause = &source.generics.where_clause;
59 let bracketed_wrapper_generics = emit_bracketed_generics(&generics);
60
61 let contract_enum_name = msg_ty.emit_msg_wrapper_name();
62 let enum_accessor = msg_ty.as_accessor_name();
63 let contract_name = StripGenerics.fold_type((*contract).clone());
64
65 let variants = interfaces.emit_glue_message_variants(msg_ty, contract);
66 let types = interfaces.emit_glue_message_types(msg_ty, contract);
67
68 let ep_name = msg_ty.emit_ep_name();
69 let messages_fn_name = Ident::new(&format!("{}_messages", ep_name), contract.span());
70 let contract_variant = quote! { #contract_name ( <#contract as #sylvia ::types::ContractApi> :: #enum_accessor ) };
71 let mut messages_call = interfaces.emit_messages_call(msg_ty);
72 messages_call.push(quote! { &#messages_fn_name() });
73
74 let variants_cnt = messages_call.len();
75
76 let dispatch_arms = interfaces.emit_dispatch_arms(msg_ty);
77
78 let dispatch_arm =
79 quote! {#contract_enum_name :: #contract_name (msg) => msg.dispatch(contract, ctx)};
80
81 let interfaces_deserialization_attempts = interfaces.emit_deserialization_attempts(msg_ty);
82
83 let contract_deserialization_attempt = quote! {
84 let msgs = &#messages_fn_name();
85 if msgs.into_iter().any(|msg| msg == &recv_msg_name) {
86 match val.deserialize_into() {
87 Ok(msg) => return Ok(Self:: #contract_name (msg)),
88 Err(err) => return Err(D::Error::custom(err)).map(Self:: #contract_name )
89 };
90 }
91 };
92
93 let ctx_type = msg_ty.emit_ctx_type(&custom.query_or_default());
94 let ret_type = msg_ty.emit_result_type(&custom.msg_or_default(), &error.error);
95
96 let mut response_schemas_calls = interfaces.emit_response_schemas_calls(msg_ty, contract);
97 response_schemas_calls
98 .push(quote! {<#contract as #sylvia ::types::ContractApi> :: #enum_accessor ::response_schemas_impl()});
99
100 let response_schemas = match msg_ty {
101 MsgType::Query => {
102 quote! {
103 #[cfg(not(target_arch = "wasm32"))]
104 impl #bracketed_wrapper_generics #sylvia ::cw_schema::QueryResponses for #contract_enum_name #bracketed_wrapper_generics #full_where_clause {
105 fn response_schemas_impl() -> std::collections::BTreeMap<String, #sylvia ::schemars::schema::RootSchema> {
106 let responses = [#(#response_schemas_calls),*];
107 responses.into_iter().flatten().collect()
108 }
109 }
110 }
111 }
112 _ => {
113 quote! {}
114 }
115 };
116
117 let modules_names = interfaces.variants_modules();
118 let variants_names = interfaces.variants_names();
119 let serde = quote! { #sylvia:: serde }.to_string();
120
121 quote! {
122 #[allow(clippy::derive_partial_eq_without_eq)]
123 #[derive(#sylvia ::serde::Serialize, Clone, Debug, PartialEq)]
124 #[serde(rename_all="snake_case", untagged)]
125 #[serde(crate = #serde )]
126 pub enum #contract_enum_name #bracketed_wrapper_generics #full_where_clause {
127 #(#variants,)*
128 #contract_variant
129 }
130
131 impl #bracketed_wrapper_generics #sylvia ::schemars::JsonSchema
137 for #contract_enum_name #bracketed_wrapper_generics #full_where_clause {
138
139 fn schema_name() -> std::string::String {
140 {
141 let res = format!(
142 "{0}",
143 std::any::type_name::<Self>()
144 );
145 res
146 }
147 }
148
149 fn json_schema(
150 gen: &mut #sylvia ::schemars::gen::SchemaGenerator,
151 ) -> #sylvia ::schemars::schema::Schema {
152 #sylvia ::schemars::schema::Schema::Object( #sylvia ::schemars::schema::SchemaObject {
153 subschemas: Some(
154 Box::new( #sylvia ::schemars::schema::SubschemaValidation {
155 any_of: Some(
156 <[_]>::into_vec(
157 Box::new([
158 #(gen.subschema_for::<#types>(),)*
159 gen.subschema_for::< <#contract as #sylvia ::types::ContractApi> :: #enum_accessor >(),
160 ]),
161 ),
162 ),
163 ..Default::default()
164 }),
165 ),
166 ..Default::default()
167 })
168 }
169 }
170
171 impl #bracketed_wrapper_generics #contract_enum_name #bracketed_wrapper_generics #full_where_clause {
172 pub fn dispatch (
173 self,
174 contract: &#contract,
175 ctx: #ctx_type,
176 ) -> #ret_type #full_where_clause {
177 const _: () = {
178 let msgs: [&[&str]; #variants_cnt] = [#(#messages_call),*];
179 #sylvia ::utils::assert_no_intersection(msgs);
180 };
181
182 match self {
183 #(#dispatch_arms,)*
184 #dispatch_arm
185 }
186 }
187 }
188
189 #response_schemas
190
191 impl<'sv_de, #(#generics,)* > #sylvia ::serde::Deserialize<'sv_de> for #contract_enum_name #bracketed_wrapper_generics #full_where_clause {
192 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
193 where D: #sylvia ::serde::Deserializer<'sv_de>,
194 {
195 use #sylvia ::serde::de::Error;
196
197 let val = #sylvia ::serde_value::Value::deserialize(deserializer)?;
198 let map = match &val {
199 #sylvia ::serde_value::Value::Map(map) => map,
200 _ => return Err(D::Error::custom("Wrong message format!"))
201 };
202 if map.len() != 1 {
203 return Err(D::Error::custom(format!("Expected exactly one message. Received {}", map.len())))
204 }
205
206 let recv_msg_name = map.into_iter().next().unwrap();
208
209 if let #sylvia ::serde_value::Value::String(recv_msg_name) = &recv_msg_name .0 {
210 #(#interfaces_deserialization_attempts)*
211 #contract_deserialization_attempt
212 }
213
214 let msgs: [&[&str]; #variants_cnt] = [#(#messages_call),*];
215 let mut err_msg = msgs.into_iter().flatten().fold(
216 format!(
219 "Unsupported message received: {}. Messages supported by this contract: ",
220 #sylvia ::serde_json::to_string(&val).unwrap_or_else(|_| String::new())
221 ),
222 |mut acc, message| acc + message + ", ",
223 );
224 err_msg.truncate(err_msg.len() - 2);
225 Err(D::Error::custom(err_msg))
226 }
227 }
228
229 impl #bracketed_wrapper_generics From<<#contract as #sylvia ::types::ContractApi>:: #enum_accessor>
230 for #contract_enum_name #bracketed_wrapper_generics #full_where_clause {
231 fn from(a: <#contract as #sylvia ::types::ContractApi>:: #enum_accessor ) -> Self {
232 Self:: #contract_name (a)
233 }
234 }
235
236 #(
237 impl #bracketed_wrapper_generics From<<#contract as #modules_names ::sv::InterfaceMessagesApi>:: #enum_accessor>
238 for #contract_enum_name #bracketed_wrapper_generics #full_where_clause {
239 fn from(a: <#contract as #modules_names ::sv::InterfaceMessagesApi>:: #enum_accessor ) -> Self {
240 Self:: #variants_names (a)
241 }
242 }
243 )*
244 }
245 }
246}