Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add support for sv::features attribute. (#446) #447

Merged
merged 2 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ version = "0.5.0"
edition = "2021"

[workspace.dependencies]
sylvia = { path = "../sylvia", features = ["sv_replies"] }
sylvia = { path = "../sylvia" }
cw-storage-plus = "2.0.0"
cw-utils = "2.0.0"
cw2 = "2.0.0"
Expand Down
1 change: 1 addition & 0 deletions examples/contracts/generic_contract/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ pub struct GenericContract<
#[sv::messages(generic as Generic: custom(msg, query))]
#[sv::messages(custom_and_generic as CustomAndGeneric)]
#[sv::custom(msg=SvCustomMsg, query=SvCustomQuery)]
#[sv::features(replies)]
impl<
InstantiateT,
Exec1T,
Expand Down
1 change: 1 addition & 0 deletions examples/contracts/generics_forwarded/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ pub struct GenericsForwardedContract<
#[sv::messages(cw1 as Cw1: custom(msg, query))]
#[sv::messages(custom_and_generic as CustomAndGeneric)]
#[sv::custom(msg=CustomMsgT, query=CustomQueryT)]
#[sv::features(replies)]
impl<
InstantiateT,
Exec1T,
Expand Down
2 changes: 0 additions & 2 deletions sylvia-derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ readme = "../README.md"
[features]
mt = []
cosmwasm_1_2 = []
sv_replies = []

[lib]
proc-macro = true
Expand Down Expand Up @@ -43,7 +42,6 @@ sylvia = { path = "../sylvia", features = [
"cosmwasm_1_2",
"cosmwasm_1_3",
"cosmwasm_1_4",
"sv_replies",
] }
serde = { workspace = true }
cosmwasm-schema = { workspace = true }
Expand Down
6 changes: 5 additions & 1 deletion sylvia-derive/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use proc_macro2::TokenStream;
use quote::quote;
use syn::{GenericParam, ItemImpl};

use crate::parser::attributes::features::SylviaFeatures;
use crate::parser::attributes::msg::MsgType;
use crate::parser::variant_descs::AsVariantDescs;
use crate::parser::{
Expand Down Expand Up @@ -46,6 +47,7 @@ pub struct ContractInput<'a> {
custom: Custom,
override_entry_points: Vec<OverrideEntryPoint>,
interfaces: Interfaces,
sv_features: SylviaFeatures,
}

impl<'a> ContractInput<'a> {
Expand All @@ -56,6 +58,7 @@ impl<'a> ContractInput<'a> {
let parsed_attrs = ParsedSylviaAttributes::new(item.attrs.iter());
let error = parsed_attrs.error_attrs.unwrap_or_default();
let custom = parsed_attrs.custom_attr.unwrap_or_default();
let sv_features = parsed_attrs.sv_features;
let override_entry_points = parsed_attrs.override_entry_point_attrs;
let interfaces = Interfaces::new(item);

Expand All @@ -66,6 +69,7 @@ impl<'a> ContractInput<'a> {
custom,
override_entry_points,
interfaces,
sv_features,
}
}

Expand Down Expand Up @@ -186,7 +190,7 @@ impl<'a> ContractInput<'a> {
}

fn emit_reply(&self) -> TokenStream {
if cfg!(feature = "sv_replies") {
if self.sv_features.replies {
let variants = MsgVariants::new(self.item.as_variants(), MsgType::Reply, &[], &None);

Reply::new(self.item, &self.generics, &variants).emit()
Expand Down
15 changes: 9 additions & 6 deletions sylvia-derive/src/contract/mt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use syn::{parse_quote, GenericParam, ItemImpl, Type};

use crate::crate_module;
use crate::parser::attributes::features::SylviaFeatures;
use crate::parser::attributes::msg::MsgType;
use crate::parser::variant_descs::AsVariantDescs;
use crate::parser::{
Expand All @@ -23,6 +24,7 @@
where_clause: &'a Option<syn::WhereClause>,
custom: &'a Custom,
override_entry_points: Vec<OverrideEntryPoint>,
sv_features: SylviaFeatures,
instantiate_variant: MsgVariants<'a, GenericParam>,
exec_variants: MsgVariants<'a, GenericParam>,
query_variants: MsgVariants<'a, GenericParam>,
Expand Down Expand Up @@ -76,11 +78,10 @@
where_clause,
);

let error_type = ParsedSylviaAttributes::new(source.attrs.iter())
.error_attrs
.unwrap_or_default()
.error;
let parsed_attrs = ParsedSylviaAttributes::new(source.attrs.iter());
let error_type = parsed_attrs.error_attrs.unwrap_or_default().error;
let error_type = parse_quote! { #error_type };
let sv_features = parsed_attrs.sv_features;

let contract_name = &source.self_ty;

Expand All @@ -92,6 +93,7 @@
contract_name,
custom,
override_entry_points,
sv_features,
instantiate_variant,
exec_variants,
query_variants,
Expand Down Expand Up @@ -478,6 +480,7 @@
contract_name,
custom,
override_entry_points,
sv_features,
generic_params,
migrate_variants,
reply_variants,
Expand Down Expand Up @@ -529,15 +532,15 @@
quote! { #contract_ident }
};

if cfg!(feature = "sv_replies") {
if sv_features.replies {

Check warning on line 535 in sylvia-derive/src/contract/mt.rs

View check run for this annotation

Codecov / codecov/patch

sylvia-derive/src/contract/mt.rs#L535

Added line #L535 was not covered by tests
quote! {
let contract = #contract_turbofish ::new();
dispatch_reply(deps, env, msg, contract).map_err(Into::into)
}
} else {
let reply_name = _reply.name().to_case(Case::Snake);
quote! {
self. #reply_name ((deps, env).into(), msg).map_err(Into::into)
self. #reply_name ((deps, env, 0, vec![], vec![]).into(), msg).map_err(Into::into)
}
}
})
Expand Down
9 changes: 7 additions & 2 deletions sylvia-derive/src/entry_points.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use crate::crate_module;
use crate::fold::StripGenerics;
use crate::parser::attributes::features::SylviaFeatures;
use crate::parser::attributes::msg::MsgType;
use crate::parser::variant_descs::AsVariantDescs;
use crate::parser::{
Expand Down Expand Up @@ -68,13 +69,15 @@
generics: Vec<&'a GenericParam>,
where_clause: &'a Option<WhereClause>,
attrs: &'a EntryPointArgs,
sv_features: SylviaFeatures,
}

impl<'a> EntryPoints<'a> {
pub fn new(source: &'a ItemImpl, attrs: &'a EntryPointArgs) -> Self {
let name = StripGenerics.fold_type(*source.self_ty.clone());
let parsed_attrs = ParsedSylviaAttributes::new(source.attrs.iter());
let override_entry_points = parsed_attrs.override_entry_point_attrs;
let sv_features = parsed_attrs.sv_features;

Check warning on line 80 in sylvia-derive/src/entry_points.rs

View check run for this annotation

Codecov / codecov/patch

sylvia-derive/src/entry_points.rs#L80

Added line #L80 was not covered by tests

let error = parsed_attrs.error_attrs.unwrap_or_default().error;

Expand All @@ -96,6 +99,7 @@
generics,
where_clause,
attrs,
sv_features,
}
}

Expand Down Expand Up @@ -172,6 +176,7 @@
error,
attrs,
reply,
sv_features,

Check warning on line 179 in sylvia-derive/src/entry_points.rs

View check run for this annotation

Codecov / codecov/patch

sylvia-derive/src/entry_points.rs#L179

Added line #L179 was not covered by tests
..
} = self;
let sylvia = crate_module();
Expand Down Expand Up @@ -201,12 +206,12 @@
_ => quote! { msg: < #contract as #sylvia ::types::ContractApi> :: #associated_name },
};
let dispatch = match msg_ty {
MsgType::Reply if cfg!(feature = "sv_replies") => quote! {
MsgType::Reply if sv_features.replies => quote! {

Check warning on line 209 in sylvia-derive/src/entry_points.rs

View check run for this annotation

Codecov / codecov/patch

sylvia-derive/src/entry_points.rs#L209

Added line #L209 was not covered by tests
let contract = #contract_turbofish ::new();
sv::dispatch_reply(deps, env, msg, contract).map_err(Into::into)
},
MsgType::Reply => quote! {
#contract_turbofish ::new(). #reply((deps, env).into(), msg).map_err(Into::into)
#contract_turbofish ::new(). #reply((deps, env, 0, vec![], vec![]).into(), msg).map_err(Into::into)

Check warning on line 214 in sylvia-derive/src/entry_points.rs

View check run for this annotation

Codecov / codecov/patch

sylvia-derive/src/entry_points.rs#L214

Added line #L214 was not covered by tests
},
_ => quote! {
msg.dispatch(& #contract_turbofish ::new() , ( #values )).map_err(Into::into)
Expand Down
17 changes: 17 additions & 0 deletions sylvia-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ fn interface_impl(_attr: TokenStream2, item: TokenStream2) -> TokenStream2 {
///
/// ##[sylvia::contract]
/// ##[sv::error(ContractError)]
/// ##[sv::features(replies)]
/// impl SvContract {
/// pub const fn new() -> Self {
/// Self {
Expand Down Expand Up @@ -621,7 +622,22 @@ fn interface_impl(_attr: TokenStream2, item: TokenStream2) -> TokenStream2 {
/// and only for message types variants that resolves in an enum field,
/// i.e. `exec`, `query` and `sudo`.
///
/// ### `sv::features(...)`
///
/// Enables additional features for the contract. Allows user to use features that
/// are considered breaking before the major release.
///
/// Currently supported features are:
/// * `replies` - enables better dispatching of `reply` as well as its auto deserialization.
/// With this feature enabled, user can use additional parameters in the
/// `sv::msg` attribute like so:
/// `#[sv::msg(reply, handlers=[scenario1, scenario2], reply_on=Success)]`.
///
/// Based on this parameters reply ids will be generated and associated with
/// proper scenario specified by the `reply_on` parameter.
///
/// User can also specify custom `data` and `payload` types that will be auto
/// deserialized from the `cosmwasm_std::Binary` type.
#[proc_macro_error]
#[proc_macro_attribute]
pub fn contract(attr: TokenStream, item: TokenStream) -> TokenStream {
Expand Down Expand Up @@ -661,6 +677,7 @@ fn contract_impl(attr: TokenStream2, item: TokenStream2) -> TokenStream2 {
///
/// ##[sylvia::entry_points]
/// ##[sylvia::contract]
/// ##[sv::features(replies)]
/// impl SvContract {
/// pub const fn new() -> Self {
/// Self
Expand Down
46 changes: 46 additions & 0 deletions sylvia-derive/src/parser/attributes/features.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use proc_macro_error::emit_error;
use syn::parse::{Parse, ParseStream, Parser};
use syn::{Error, Ident, MetaList, Result, Token};

/// Type wrapping data parsed from `sv::features` attribute.
#[derive(Debug, Default)]
pub struct SylviaFeatures {
/// Enables better dispatching and deserialization for replies.
pub replies: bool,
}

impl SylviaFeatures {
pub fn new(attr: &MetaList) -> Result<Self> {
SylviaFeatures::parse
.parse2(attr.tokens.clone())
.map_err(|err| {
emit_error!(err.span(), err);
err

Check warning on line 18 in sylvia-derive/src/parser/attributes/features.rs

View check run for this annotation

Codecov / codecov/patch

sylvia-derive/src/parser/attributes/features.rs#L17-L18

Added lines #L17 - L18 were not covered by tests
})
}
}

impl Parse for SylviaFeatures {
fn parse(input: ParseStream) -> Result<Self> {
let mut features = Self::default();

while !input.is_empty() {
let feature: Ident = input.parse()?;
match feature.to_string().as_str() {
"replies" => features.replies = true,
_ => {
return Err(Error::new(
feature.span(),
"Invalid feature.\n= note: Supported features for contract macro: [`replies`].\n",

Check warning on line 34 in sylvia-derive/src/parser/attributes/features.rs

View check run for this annotation

Codecov / codecov/patch

sylvia-derive/src/parser/attributes/features.rs#L32-L34

Added lines #L32 - L34 were not covered by tests
))
}
}
if !input.peek(Token![,]) {
break;
}
let _: Token![,] = input.parse()?;

Check warning on line 41 in sylvia-derive/src/parser/attributes/features.rs

View check run for this annotation

Codecov / codecov/patch

sylvia-derive/src/parser/attributes/features.rs#L41

Added line #L41 was not covered by tests
}

Ok(features)
}
}
10 changes: 10 additions & 0 deletions sylvia-derive/src/parser/attributes/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
//! Module defining parsing of Sylvia attributes.
//! Every Sylvia attribute should be prefixed with `sv::`

use features::SylviaFeatures;
use proc_macro_error::emit_error;
use syn::spanned::Spanned;
use syn::{Attribute, MetaList, PathSegment};

pub mod attr;
pub mod custom;
pub mod error;
pub mod features;
pub mod messages;
pub mod msg;
pub mod override_entry_point;
Expand All @@ -31,6 +33,7 @@ pub enum SylviaAttribute {
VariantAttrs,
MsgAttrs,
Payload,
Features,
}

impl SylviaAttribute {
Expand All @@ -53,6 +56,7 @@ impl SylviaAttribute {
"attr" => Some(Self::VariantAttrs),
"msg_attr" => Some(Self::MsgAttrs),
"payload" => Some(Self::Payload),
"features" => Some(Self::Features),
_ => None,
}
}
Expand All @@ -69,6 +73,7 @@ pub struct ParsedSylviaAttributes {
pub override_entry_point_attrs: Vec<OverrideEntryPoint>,
pub variant_attrs_forward: Vec<VariantAttrForwarding>,
pub msg_attrs_forward: Vec<MsgAttrForwarding>,
pub sv_features: SylviaFeatures,
}

impl ParsedSylviaAttributes {
Expand Down Expand Up @@ -167,6 +172,11 @@ impl ParsedSylviaAttributes {
note = attr.span() => "The `sv::payload` should be used as a prefix for `Binary` payload.";
);
}
SylviaAttribute::Features => {
if let Ok(features) = SylviaFeatures::new(attr) {
self.sv_features = features;
}
}
}
}
}
2 changes: 0 additions & 2 deletions sylvia/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ cosmwasm_2_0 = [
"cw-multi-test/cosmwasm_2_0",
"cosmwasm_1_4",
]
# Enables better replies
sv_replies = ["sylvia-derive/sv_replies"]

[dependencies]
sylvia-derive = { workspace = true }
Expand Down
Loading
Loading