Repository: CosmWasm/sylvia
Pull request #121 (2023-05-17 08:56:33)

Remote structure
PR1 of #28 


Patch:
diff --git a/sylvia-derive/src/input.rs b/sylvia-derive/src/input.rs
index 656ad8e..687d9bd 100644
--- a/sylvia-derive/src/input.rs
+++ b/sylvia-derive/src/input.rs
@@ -9,6 +9,7 @@ use crate::crate_module;
 use crate::message::{ContractEnumMessage, EnumMessage, GlueMessage, StructMessage};
 use crate::multitest::{MultitestHelpers, TraitMultitestHelpers};
 use crate::parser::{ContractArgs, ContractErrorAttr, InterfaceArgs, MsgType};
+use crate::remote::Remote;
 
 /// Preprocessed `interface` macro input
 pub struct TraitInput<'a> {
@@ -53,21 +54,33 @@ impl<'a> TraitInput<'a> {
     pub fn process(&self) -> TokenStream {
         let messages = self.emit_messages();
         let multitest_helpers = self.emit_helpers();
+        let remote = Remote::for_interface().emit();
 
         if let Some(module) = &self.attributes.module {
-            quote! {
-                pub mod #module {
-                    use super::*;
+            #[cfg(not(tarpaulin_include))]
+            {
+                quote! {
+                    pub mod #module {
+                        use super::*;
 
-                    #messages
+                        #messages
 
-                    #multitest_helpers
+                        #multitest_helpers
+
+                        #remote
+                    }
                 }
             }
         } else {
-            quote! {
-                #messages
-                #multitest_helpers
+            #[cfg(not(tarpaulin_include))]
+            {
+                quote! {
+                    #messages
+
+                    #multitest_helpers
+
+                    #remote
+                }
             }
         }
     }
@@ -93,10 +106,13 @@ impl<'a> TraitInput<'a> {
             self.attributes,
         );
 
-        quote! {
-            #exec
+        #[cfg(not(tarpaulin_include))]
+        {
+            quote! {
+                #exec
 
-            #query
+                #query
+            }
         }
     }
 
@@ -148,18 +164,26 @@ impl<'a> ImplInput<'a> {
         }
 
         let messages = self.emit_messages();
+        let remote = Remote::for_contract(self.item).emit();
+
+        #[cfg(not(tarpaulin_include))]
         let code = quote! {
             #messages
 
             #multitest_helpers
+
+            #remote
         };
 
         if let Some(module) = &self.attributes.module {
-            quote! {
-                pub mod #module {
-                    use super::*;
+            #[cfg(not(tarpaulin_include))]
+            {
+                quote! {
+                    pub mod #module {
+                        use super::*;
 
-                    #code
+                        #code
+                    }
                 }
             }
         } else {
@@ -177,18 +201,21 @@ impl<'a> ImplInput<'a> {
         let exec = self.emit_glue_msg(&Ident::new("ExecMsg", Span::mixed_site()), MsgType::Exec);
         let query = self.emit_glue_msg(&Ident::new("QueryMsg", Span::mixed_site()), MsgType::Query);
 
-        quote! {
-            #instantiate
+        #[cfg(not(tarpaulin_include))]
+        {
+            quote! {
+                #instantiate
 
-            #exec_impl
+                #exec_impl
 
-            #query_impl
+                #query_impl
 
-            #migrate
+                #migrate
 
-            #exec
+                #exec
 
-            #query
+                #query
+            }
         }
     }
 
diff --git a/sylvia-derive/src/lib.rs b/sylvia-derive/src/lib.rs
index 4d41ac8..91a274c 100644
--- a/sylvia-derive/src/lib.rs
+++ b/sylvia-derive/src/lib.rs
@@ -11,6 +11,7 @@ mod input;
 mod message;
 mod multitest;
 mod parser;
+mod remote;
 mod strip_generics;
 mod strip_input;
 mod utils;
diff --git a/sylvia-derive/src/remote.rs b/sylvia-derive/src/remote.rs
new file mode 100644
index 0000000..1155065
--- /dev/null
+++ b/sylvia-derive/src/remote.rs
@@ -0,0 +1,79 @@
+use proc_macro2::TokenStream;
+use proc_macro_error::emit_error;
+use quote::quote;
+use syn::parse::{Parse, Parser};
+use syn::spanned::Spanned;
+use syn::ItemImpl;
+
+use crate::crate_module;
+use crate::parser::ContractMessageAttr;
+
+pub struct Remote {
+    interfaces: Vec<ContractMessageAttr>,
+}
+
+impl Remote {
+    pub fn for_contract(source: &ItemImpl) -> Self {
+        let interfaces: Vec<_> = source
+            .attrs
+            .iter()
+            .filter(|attr| attr.path.is_ident("messages"))
+            .filter_map(|attr| {
+                let interface = match ContractMessageAttr::parse.parse2(attr.tokens.clone()) {
+                    Ok(interface) => interface,
+                    Err(err) => {
+                        emit_error!(attr.span(), err);
+                        return None;
+                    }
+                };
+
+                Some(interface)
+            })
+            .collect();
+        Self { interfaces }
+    }
+
+    pub fn for_interface() -> Self {
+        Self { interfaces: vec![] }
+    }
+
+    pub fn emit(&self) -> TokenStream {
+        let sylvia = crate_module();
+
+        let from_implementations = self.interfaces.iter().map(|interface| {
+            let ContractMessageAttr { module, .. } = interface;
+
+            quote! {
+                impl<'a> From<&'a Remote<'a>> for #module ::Remote<'a> {
+                    fn from(remote: &'a Remote) -> Self {
+                        #module ::Remote::borrowed(remote.as_ref())
+                    }
+                }
+            }
+        });
+
+        quote! {
+            pub struct Remote<'a>(std::borrow::Cow<'a, #sylvia ::cw_std::Addr>);
+
+            impl Remote<'static> {
+                pub fn new(addr: #sylvia ::cw_std::Addr) -> Self {
+                    Self(std::borrow::Cow::Owned(addr))
+                }
+            }
+
+            impl<'a> Remote<'a> {
+                pub fn borrowed(addr: &'a #sylvia ::cw_std::Addr) -> Self {
+                    Self(std::borrow::Cow::Borrowed(addr))
+                }
+            }
+
+            impl<'a> AsRef<#sylvia ::cw_std::Addr> for Remote<'a> {
+                fn as_ref(&self) -> &#sylvia ::cw_std::Addr {
+                    &self.0
+                }
+            }
+
+            #(#from_implementations)*
+        }
+    }
+}


Test patch:
diff --git a/sylvia/tests/remote.rs b/sylvia/tests/remote.rs
new file mode 100644
index 0000000..d2b959f
--- /dev/null
+++ b/sylvia/tests/remote.rs
@@ -0,0 +1,56 @@
+use cosmwasm_std::{Response, StdResult};
+use sylvia::{contract, types::InstantiateCtx};
+
+pub struct SomeContract;
+
+pub mod some_interface {
+    use cosmwasm_std::StdError;
+    use sylvia::interface;
+
+    #[interface]
+    pub trait SomeInterface {
+        type Error: From<StdError>;
+    }
+
+    impl SomeInterface for super::SomeContract {
+        type Error = StdError;
+    }
+}
+
+#[contract]
+#[messages(some_interface as SomeInterface)]
+impl SomeContract {
+    #[allow(clippy::new_without_default)]
+    pub fn new() -> Self {
+        Self
+    }
+
+    #[msg(instantiate)]
+    pub fn instantiate(&self, _ctx: InstantiateCtx) -> StdResult<Response> {
+        Ok(Response::new())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use cosmwasm_std::Addr;
+
+    use crate::some_interface;
+
+    #[test]
+    fn remote_generation() {
+        // interface
+        let _ = some_interface::Remote::new(Addr::unchecked("some_interface"));
+        let addr = Addr::unchecked("some_interface");
+        let _ = some_interface::Remote::borrowed(&addr);
+
+        // contract
+        let new_remote = crate::Remote::new(Addr::unchecked("some_contract"));
+        let addr = Addr::unchecked("some_contract");
+        let borrowed_remote = crate::Remote::borrowed(&addr);
+        assert_eq!(&Addr::unchecked("some_contract"), borrowed_remote.as_ref());
+
+        let _ = some_interface::Remote::from(&borrowed_remote);
+        let _ = some_interface::Remote::from(&new_remote);
+    }
+}