Repository: CosmWasm/sylvia
Pull request #391 (2024-07-01 12:12:16)

feat: `From` implementation for contract messages of interface messages

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/CosmWasm/sylvia/pull/391?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 85.89%. Comparing base [(`55e9667`)](https://app.codecov.io/gh/CosmWasm/sylvia/commit/55e96679c61e1fa4b40db2e9efdeb0b473738669?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm) to head [(`dc6f0ad`)](https://app.codecov.io/gh/CosmWasm/sylvia/commit/dc6f0ad0550b0d57a0689e4ca3f5af2c292002bd?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm).

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main     #391      +/-   ##
==========================================
+ Coverage   85.83%   85.89%   +0.05%     
==========================================
  Files          40       40              
  Lines        2866     2878      +12     
==========================================
+ Hits         2460     2472      +12     
  Misses        406      406              
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/CosmWasm/sylvia/pull/391?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm).


Patch:
diff --git a/sylvia-derive/src/interfaces.rs b/sylvia-derive/src/interfaces.rs
index 3cd104d..cf6e788 100644
--- a/sylvia-derive/src/interfaces.rs
+++ b/sylvia-derive/src/interfaces.rs
@@ -1,7 +1,7 @@
 use proc_macro2::{Ident, TokenStream};
 use quote::quote;
 use syn::spanned::Spanned;
-use syn::{ItemImpl, Type};
+use syn::{ItemImpl, Path, Type};
 
 use crate::crate_module;
 use crate::parser::attributes::msg::MsgType;
@@ -133,4 +133,12 @@ impl Interfaces {
             }
         }).collect()
     }
+
+    pub fn variants_names(&self) -> impl Iterator<Item = &Ident> {
+        self.interfaces.iter().map(|interface| &interface.variant)
+    }
+
+    pub fn variants_modules(&self) -> impl Iterator<Item = &Path> {
+        self.interfaces.iter().map(|interface| &interface.module)
+    }
 }
diff --git a/sylvia-derive/src/message.rs b/sylvia-derive/src/message.rs
index cb70e54..e2a3397 100644
--- a/sylvia-derive/src/message.rs
+++ b/sylvia-derive/src/message.rs
@@ -952,6 +952,9 @@ impl<'a> GlueMessage<'a> {
             }
         };
 
+        let modules_names = interfaces.variants_modules();
+        let variants_names = interfaces.variants_names();
+
         quote! {
             #[allow(clippy::derive_partial_eq_without_eq)]
             #[derive(#sylvia ::serde::Serialize, Clone, Debug, PartialEq)]
@@ -1058,6 +1061,22 @@ impl<'a> GlueMessage<'a> {
                     Err(D::Error::custom(err_msg))
                 }
             }
+
+            impl #bracketed_wrapper_generics From<<#contract as #sylvia ::types::ContractApi>:: #enum_accessor>
+                for #contract_enum_name #bracketed_wrapper_generics #full_where_clause {
+                fn from(a: <#contract as #sylvia ::types::ContractApi>:: #enum_accessor ) -> Self {
+                    Self:: #contract_name (a)
+                }
+            }
+
+            #(
+            impl #bracketed_wrapper_generics From<<#contract as #modules_names ::sv::InterfaceMessagesApi>:: #enum_accessor>
+                for #contract_enum_name #bracketed_wrapper_generics #full_where_clause {
+                fn from(a: <#contract as #modules_names ::sv::InterfaceMessagesApi>:: #enum_accessor ) -> Self {
+                    Self:: #variants_names (a)
+                }
+            }
+            )*
         }
     }
 }


Test patch:
diff --git a/sylvia/tests/interface_impl.rs b/sylvia/tests/interface_impl.rs
new file mode 100644
index 0000000..6bd6615
--- /dev/null
+++ b/sylvia/tests/interface_impl.rs
@@ -0,0 +1,137 @@
+use cosmwasm_std::{Response, StdError, StdResult};
+use sylvia::contract;
+use sylvia::types::{ExecCtx, InstantiateCtx, QueryCtx, SudoCtx};
+
+mod interface1 {
+    use cosmwasm_std::{Response, StdError, StdResult};
+    use sylvia::interface;
+    use sylvia::types::{ExecCtx, QueryCtx, SudoCtx};
+
+    #[interface]
+    #[sv::custom(msg=cosmwasm_std::Empty, query=cosmwasm_std::Empty)]
+    pub trait SylviaInterface1 {
+        type Error: From<StdError>;
+
+        #[sv::msg(exec)]
+        fn interface1_method_exec(&self, _ctx: ExecCtx) -> StdResult<Response>;
+
+        #[sv::msg(query)]
+        fn interface1_method_query(&self, _ctx: QueryCtx) -> StdResult<Response>;
+
+        #[sv::msg(sudo)]
+        fn interface1_method_sudo(&self, _ctx: SudoCtx) -> StdResult<Response>;
+    }
+}
+
+mod interface2 {
+    use cosmwasm_std::{Response, StdError, StdResult};
+    use sylvia::interface;
+    use sylvia::types::{ExecCtx, QueryCtx, SudoCtx};
+
+    #[interface]
+    #[sv::custom(msg=cosmwasm_std::Empty, query=cosmwasm_std::Empty)]
+    pub trait SylviaInterface2 {
+        type Error: From<StdError>;
+
+        #[sv::msg(exec)]
+        fn interface2_method_exec(&self, _ctx: ExecCtx) -> StdResult<Response>;
+
+        #[sv::msg(query)]
+        fn interface2_method_query(&self, _ctx: QueryCtx) -> StdResult<Response>;
+
+        #[sv::msg(sudo)]
+        fn interface2_method_sudo(&self, _ctx: SudoCtx) -> StdResult<Response>;
+    }
+}
+
+pub struct Contract;
+
+#[contract]
+#[sv::messages(interface1)]
+#[sv::messages(interface2)]
+impl Contract {
+    pub fn new() -> Self {
+        Self
+    }
+
+    #[sv::msg(instantiate)]
+    fn instantiate(&self, _ctx: InstantiateCtx) -> StdResult<Response> {
+        Ok(Response::new())
+    }
+
+    #[sv::msg(exec)]
+    fn contract_method_exec(&self, _ctx: ExecCtx) -> StdResult<Response> {
+        Ok(Response::new())
+    }
+
+    #[sv::msg(query)]
+    fn contract_method_query(&self, _ctx: QueryCtx) -> StdResult<Response> {
+        Ok(Response::new())
+    }
+
+    #[sv::msg(sudo)]
+    fn contract_method_sudo(&self, _ctx: SudoCtx) -> StdResult<Response> {
+        Ok(Response::new())
+    }
+}
+
+impl interface1::SylviaInterface1 for Contract {
+    type Error = StdError;
+
+    fn interface1_method_exec(&self, _ctx: ExecCtx) -> StdResult<Response> {
+        Ok(Response::new())
+    }
+
+    fn interface1_method_query(&self, _ctx: QueryCtx) -> StdResult<Response> {
+        Ok(Response::new())
+    }
+
+    fn interface1_method_sudo(&self, _ctx: SudoCtx) -> StdResult<Response> {
+        Ok(Response::new())
+    }
+}
+
+impl interface2::SylviaInterface2 for Contract {
+    type Error = StdError;
+
+    fn interface2_method_exec(&self, _ctx: ExecCtx) -> StdResult<Response> {
+        Ok(Response::new())
+    }
+
+    fn interface2_method_query(&self, _ctx: QueryCtx) -> StdResult<Response> {
+        Ok(Response::new())
+    }
+
+    fn interface2_method_sudo(&self, _ctx: SudoCtx) -> StdResult<Response> {
+        Ok(Response::new())
+    }
+}
+
+impl Default for Contract {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+#[test]
+fn check_from_trait_implementations() {
+    let _ =
+        sv::ContractExecMsg::from(interface1::sv::SylviaInterface1ExecMsg::Interface1MethodExec {});
+    let _ = sv::ContractQueryMsg::from(
+        interface1::sv::SylviaInterface1QueryMsg::Interface1MethodQuery {},
+    );
+    let _ =
+        sv::ContractSudoMsg::from(interface1::sv::SylviaInterface1SudoMsg::Interface1MethodSudo {});
+
+    let _ =
+        sv::ContractExecMsg::from(interface2::sv::SylviaInterface2ExecMsg::Interface2MethodExec {});
+    let _ = sv::ContractQueryMsg::from(
+        interface2::sv::SylviaInterface2QueryMsg::Interface2MethodQuery {},
+    );
+    let _ =
+        sv::ContractSudoMsg::from(interface2::sv::SylviaInterface2SudoMsg::Interface2MethodSudo {});
+
+    let _ = sv::ContractExecMsg::from(sv::ExecMsg::ContractMethodExec {});
+    let _ = sv::ContractQueryMsg::from(sv::QueryMsg::ContractMethodQuery {});
+    let _ = sv::ContractSudoMsg::from(sv::SudoMsg::ContractMethodSudo {});
+}