Repository: CosmWasm/sylvia
Pull request #288 (2024-01-18 16:57:56)

feat: Make as Variant optional for #[messages(...)] attribtue

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/CosmWasm/sylvia/pull/288?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:
> Comparison is base [(`511d88b`)](https://app.codecov.io/gh/CosmWasm/sylvia/commit/511d88b756fada7fce74ffb33d00fc71aacc4a46?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm) 86.56% compared to head [(`277162e`)](https://app.codecov.io/gh/CosmWasm/sylvia/pull/288?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm) 81.15%.


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


```diff
@@            Coverage Diff             @@
##             main     #288      +/-   ##
==========================================
- Coverage   86.56%   81.15%   -5.41%     
==========================================
  Files          25       25              
  Lines        1742     1725      -17     
==========================================
- Hits         1508     1400     -108     
- Misses        234      325      +91     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/CosmWasm/sylvia/pull/288?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/README.md b/README.md
index a8335dc..f00b2c4 100644
--- a/README.md
+++ b/README.md
@@ -350,6 +350,11 @@ pub struct MyContract<'a> {
 
 #[contract]
 #[messages(group as Group)]
+// Alternatively:
+// It's not needed to provide the interface's
+// name if it's corresponds to the last segment
+// of the module path:
+// #[messages(group)]
 impl group::Group for MyContract<'_> {
     type Error = ContractError;
 
@@ -385,7 +390,10 @@ matching the trait name, but written "snake_case" instead of CamelCase. Here I h
 `group` module for the `Group` trait, but the `CrossStaking` trait should be placed
 in its own `cross_staking` module (note the underscore). This is a requirement right
 now - Sylvia generates all the messages and boilerplate in this module and will try
-to access them through this module.
+to access them through this module. If the interface's name is a camel-case
+version of the last module path's segment, the `as InterfaceName` can be omitted.
+F.e. `#[messages(cw1 as Cw1)]` can be reduced to `#[messages(cw1)]`
+
 
 Then there is the `Error` type embedded in the trait - it is also needed there,
 and the trait bound here has to be at least `From<StdError>`, as Sylvia might
diff --git a/sylvia-derive/src/parser.rs b/sylvia-derive/src/parser.rs
index 0fb39c6..2856ac8 100644
--- a/sylvia-derive/src/parser.rs
+++ b/sylvia-derive/src/parser.rs
@@ -1,5 +1,6 @@
+use convert_case::{Case, Casing};
 use proc_macro2::{Punct, TokenStream};
-use proc_macro_error::emit_error;
+use proc_macro_error::{emit_error, emit_warning};
 use quote::quote;
 use syn::fold::Fold;
 use syn::parse::{Error, Nothing, Parse, ParseBuffer, ParseStream, Parser};
@@ -349,18 +350,33 @@ impl Parse for ContractMessageAttr {
         let generics = extract_generics_from_path(&module);
         let module = StripGenerics.fold_path(module);
 
-        let _: Token![as] = input.parse()?;
-        let variant = input.parse()?;
-
+        let variant = if input.parse::<Token![as]>().is_ok() {
+            let variant: Ident = input.parse()?;
+            if Some(variant.to_string())
+                == module
+                    .segments
+                    .last()
+                    .map(|name| name.ident.to_string().to_case(Case::UpperCamel))
+            {
+                emit_warning!(
+                    variant.span(), "Redundant `as {}`.", variant;
+                    note = "Interface name is a camel case version of the path and can be auto deduced."
+                )
+            }
+            variant
+        } else if let Some(module_name) = &module.segments.last() {
+            let interface_name = module_name.ident.to_string().to_case(Case::UpperCamel);
+            syn::Ident::new(&interface_name, module.span())
+        } else {
+            Ident::new("", module.span())
+        };
         let customs = interface_has_custom(input)?;
-
         if !input.is_empty() {
             return Err(Error::new(
                 input.span(),
-                "Unexpected token on the end of `message` attribtue",
+                "Unexpected token on the end of `messages` attribtue",
             ));
         }
-
         Ok(Self {
             module,
             variant,


Test patch:
diff --git a/sylvia/tests/custom_msg.rs b/sylvia/tests/custom_msg.rs
index 55e6693..0eddd1c 100644
--- a/sylvia/tests/custom_msg.rs
+++ b/sylvia/tests/custom_msg.rs
@@ -50,7 +50,7 @@ mod impl_some_interface {
     use crate::{MyMsg, SomeResponse};
 
     #[contract(module=crate)]
-    #[messages(crate::some_interface as SomeInterface)]
+    #[messages(crate::some_interface)]
     #[sv::custom(msg=MyMsg)]
     impl SomeInterface for crate::MyContract {
         type Error = StdError;
@@ -93,7 +93,7 @@ mod impl_interface {
     use sylvia::types::ExecCtx;
 
     #[contract(module=crate)]
-    #[messages(crate::interface as Interface)]
+    #[messages(crate::interface)]
     #[sv::custom(msg=MyMsg)]
     impl Interface for crate::MyContract {
         type Error = StdError;
@@ -127,7 +127,7 @@ mod impl_other_interface {
     use sylvia::types::ExecCtx;
 
     #[contract(module=crate)]
-    #[messages(crate::other_interface as OtherInterface)]
+    #[messages(crate::other_interface)]
     #[sv::custom(msg=crate::MyMsg)]
     impl OtherInterface for crate::MyContract {
         type Error = StdError;
@@ -162,7 +162,7 @@ mod impl_associated_interface {
     use sylvia::types::ExecCtx;
 
     #[contract(module=crate)]
-    #[messages(crate::associated_interface as AssociatedInterface)]
+    #[messages(crate::associated_interface)]
     #[sv::custom(msg=MyMsg)]
     impl AssociatedInterface for crate::MyContract {
         type Error = StdError;
@@ -176,10 +176,10 @@ mod impl_associated_interface {
 }
 
 #[contract]
-#[messages(some_interface as SomeInterface)]
-#[messages(other_interface as OtherInterface: custom(msg))]
-#[messages(associated_interface as AssociatedInterface)]
-#[messages(interface as Interface)]
+#[messages(some_interface)]
+#[messages(other_interface: custom(msg))]
+#[messages(associated_interface)]
+#[messages(interface)]
 #[sv::custom(msg=MyMsg)]
 impl MyContract {
     #[allow(clippy::new_without_default)]
diff --git a/sylvia/tests/custom_query.rs b/sylvia/tests/custom_query.rs
index 00c33d3..30d92ac 100644
--- a/sylvia/tests/custom_query.rs
+++ b/sylvia/tests/custom_query.rs
@@ -49,7 +49,7 @@ mod impl_interface {
     use crate::{MyQuery, OtherQuery, SomeResponse};
 
     #[contract(module=crate)]
-    #[messages(crate::interface as Interface)]
+    #[messages(crate::interface)]
     #[sv::custom(query=MyQuery)]
     impl crate::interface::Interface for crate::MyContract {
         type Error = StdError;
@@ -97,7 +97,7 @@ mod impl_some_interface {
     use crate::{MyQuery, SomeResponse};
 
     #[contract(module=crate)]
-    #[messages(crate::some_interface as SomeInterface)]
+    #[messages(crate::some_interface)]
     #[sv::custom(query=MyQuery)]
     impl super::some_interface::SomeInterface for crate::MyContract {
         type Error = StdError;
@@ -143,7 +143,7 @@ mod impl_associated_type_interface {
     use sylvia_derive::contract;
 
     #[contract(module=crate)]
-    #[messages(crate::associated_type_interface as AssociatedTypeInterface)]
+    #[messages(crate::associated_type_interface)]
     impl AssociatedTypeInterface for crate::MyContract {
         type Error = StdError;
         type QueryC = MyQuery;
@@ -188,7 +188,7 @@ mod impl_default_query_interface {
     use sylvia_derive::contract;
 
     #[contract(module=crate)]
-    #[messages(crate::default_query_interface as DefaultQueryInterface)]
+    #[messages(crate::default_query_interface)]
     #[sv::custom(query=MyQuery)]
     impl DefaultQueryInterface for crate::MyContract {
         type Error = StdError;
@@ -206,10 +206,10 @@ mod impl_default_query_interface {
 }
 
 #[contract]
-#[messages(some_interface as SomeInterface)]
-#[messages(associated_type_interface as AssociatedTypeInterface)]
-#[messages(interface as Interface)]
-#[messages(default_query_interface as DefaultQueryInterface: custom(query))]
+#[messages(some_interface)]
+#[messages(associated_type_interface)]
+#[messages(interface)]
+#[messages(default_query_interface: custom(query))]
 #[sv::custom(query=MyQuery)]
 impl MyContract {
     #[allow(clippy::new_without_default)]
diff --git a/sylvia/tests/dispatching.rs b/sylvia/tests/dispatching.rs
index 3e0c096..44cd548 100644
--- a/sylvia/tests/dispatching.rs
+++ b/sylvia/tests/dispatching.rs
@@ -55,7 +55,7 @@ mod impl_interface {
     use crate::{EmptyQueryResponse, QueryResponse};
 
     #[sylvia::contract(module = crate::contract)]
-    #[messages(crate::interface as Interface)]
+    #[messages(crate::interface)]
     impl crate::interface::Interface for crate::contract::Contract {
         type Error = StdError;
 
@@ -115,7 +115,7 @@ mod contract {
     #[allow(dead_code)]
     #[cfg(not(tarpaulin_include))]
     #[contract]
-    #[messages(crate::interface as Interface)]
+    #[messages(crate::interface)]
     impl Contract {
         fn new() -> Self {
             Self::default()
diff --git a/sylvia/tests/querier.rs b/sylvia/tests/querier.rs
index 9a78ed3..e453490 100644
--- a/sylvia/tests/querier.rs
+++ b/sylvia/tests/querier.rs
@@ -46,7 +46,7 @@ pub mod impl_counter {
     use sylvia::types::{ExecCtx, QueryCtx};
 
     #[contract(module=crate)]
-    #[messages(crate::counter as Counter)]
+    #[messages(crate::counter)]
     impl Counter for super::CounterContract<'_> {
         type Error = StdError;
 
@@ -98,7 +98,7 @@ pub struct CounterContract<'a> {
 }
 
 #[contract]
-#[messages(counter as Counter)]
+#[messages(counter)]
 impl CounterContract<'_> {
     #[allow(clippy::new_without_default)]
     pub fn new() -> Self {
diff --git a/sylvia/tests/remote.rs b/sylvia/tests/remote.rs
index 4c93708..6245d01 100644
--- a/sylvia/tests/remote.rs
+++ b/sylvia/tests/remote.rs
@@ -20,7 +20,7 @@ pub mod some_interface {
 }
 
 #[contract]
-#[messages(some_interface as SomeInterface)]
+#[messages(some_interface)]
 impl SomeContract {
     #[allow(clippy::new_without_default)]
     pub fn new() -> Self {