Skip to content

Commit

Permalink
Merge pull request #2070 from CosmWasm/aw/cosmwasm-schema-configurable
Browse files Browse the repository at this point in the history
Make crate for `cosmwasm-schema` configurable
  • Loading branch information
aumetra authored Mar 26, 2024
2 parents 763d852 + 77e2134 commit 66028ce
Show file tree
Hide file tree
Showing 5 changed files with 303 additions and 72 deletions.
153 changes: 120 additions & 33 deletions packages/schema-derive/src/cw_serde.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,59 @@
use crate::error::bail;
use quote::{quote, ToTokens};
use syn::DeriveInput;
use syn::{
parse::{Parse, ParseStream},
parse_quote,
punctuated::Punctuated,
DeriveInput, MetaNameValue, Token,
};

pub struct Options {
crate_path: syn::Path,
}

impl Default for Options {
fn default() -> Self {
Self {
crate_path: parse_quote!(::cosmwasm_schema),
}
}
}

impl Parse for Options {
fn parse(input: ParseStream) -> syn::Result<Self> {
let mut acc = Self::default();
let params = Punctuated::<MetaNameValue, Token![,]>::parse_terminated(input)?;
for param in params {
if param.path.is_ident("crate") {
let path_as_string: syn::LitStr = syn::parse2(param.value.to_token_stream())?;
acc.crate_path = path_as_string.parse()?
} else {
bail!(param, "unknown option");
}
}

Ok(acc)
}
}

pub fn cw_serde_impl(options: Options, input: DeriveInput) -> syn::Result<DeriveInput> {
let crate_path = &options.crate_path;
let crate_path_displayable = crate_path.to_token_stream();
let serde_path = format!("{crate_path_displayable}::serde");
let schemars_path = format!("{crate_path_displayable}::schemars");

pub fn cw_serde_impl(input: DeriveInput) -> syn::Result<DeriveInput> {
let mut stream = quote! {
#[derive(
::cosmwasm_schema::serde::Serialize,
::cosmwasm_schema::serde::Deserialize,
#crate_path::serde::Serialize,
#crate_path::serde::Deserialize,
::std::clone::Clone,
::std::fmt::Debug,
::std::cmp::PartialEq,
::cosmwasm_schema::schemars::JsonSchema
#crate_path::schemars::JsonSchema
)]
#[allow(clippy::derive_partial_eq_without_eq)] // Allow users of `#[cw_serde]` to not implement Eq without clippy complaining
#[serde(deny_unknown_fields, crate = "::cosmwasm_schema::serde")]
#[schemars(crate = "::cosmwasm_schema::schemars")]
#[serde(deny_unknown_fields, crate = #serde_path)]
#[schemars(crate = #schemars_path)]
};

match input.data {
Expand All @@ -35,13 +74,52 @@ mod tests {
use syn::parse_quote;

#[test]
fn structs() {
let expanded = cw_serde_impl(parse_quote! {
fn crate_rename() {
let expanded = cw_serde_impl(
Options {
crate_path: parse_quote!(::my_crate::cw_schema),
},
parse_quote! {
pub struct InstantiateMsg {
pub verifier: String,
pub beneficiary: String,
}
},
)
.unwrap();

let expected = parse_quote! {
#[derive(
::my_crate::cw_schema::serde::Serialize,
::my_crate::cw_schema::serde::Deserialize,
::std::clone::Clone,
::std::fmt::Debug,
::std::cmp::PartialEq,
::my_crate::cw_schema::schemars::JsonSchema
)]
#[allow(clippy::derive_partial_eq_without_eq)]
#[serde(deny_unknown_fields, crate = ":: my_crate :: cw_schema::serde")]
#[schemars(crate = ":: my_crate :: cw_schema::schemars")]
pub struct InstantiateMsg {
pub verifier: String,
pub beneficiary: String,
}
})
};

assert_eq!(expanded, expected);
}

#[test]
fn structs() {
let expanded = cw_serde_impl(
Options::default(),
parse_quote! {
pub struct InstantiateMsg {
pub verifier: String,
pub beneficiary: String,
}
},
)
.unwrap();

let expected = parse_quote! {
Expand All @@ -54,8 +132,8 @@ mod tests {
::cosmwasm_schema::schemars::JsonSchema
)]
#[allow(clippy::derive_partial_eq_without_eq)]
#[serde(deny_unknown_fields, crate = "::cosmwasm_schema::serde")]
#[schemars(crate = "::cosmwasm_schema::schemars")]
#[serde(deny_unknown_fields, crate = ":: cosmwasm_schema::serde")]
#[schemars(crate = ":: cosmwasm_schema::schemars")]
pub struct InstantiateMsg {
pub verifier: String,
pub beneficiary: String,
Expand All @@ -67,9 +145,12 @@ mod tests {

#[test]
fn empty_struct() {
let expanded = cw_serde_impl(parse_quote! {
pub struct InstantiateMsg {}
})
let expanded = cw_serde_impl(
Options::default(),
parse_quote! {
pub struct InstantiateMsg {}
},
)
.unwrap();

let expected = parse_quote! {
Expand All @@ -82,8 +163,8 @@ mod tests {
::cosmwasm_schema::schemars::JsonSchema
)]
#[allow(clippy::derive_partial_eq_without_eq)]
#[serde(deny_unknown_fields, crate = "::cosmwasm_schema::serde")]
#[schemars(crate = "::cosmwasm_schema::schemars")]
#[serde(deny_unknown_fields, crate = ":: cosmwasm_schema::serde")]
#[schemars(crate = ":: cosmwasm_schema::schemars")]
pub struct InstantiateMsg {}
};

Expand All @@ -92,14 +173,17 @@ mod tests {

#[test]
fn enums() {
let expanded = cw_serde_impl(parse_quote! {
pub enum SudoMsg {
StealFunds {
recipient: String,
amount: Vec<Coin>,
},
}
})
let expanded = cw_serde_impl(
Options::default(),
parse_quote! {
pub enum SudoMsg {
StealFunds {
recipient: String,
amount: Vec<Coin>,
},
}
},
)
.unwrap();

let expected = parse_quote! {
Expand All @@ -112,8 +196,8 @@ mod tests {
::cosmwasm_schema::schemars::JsonSchema
)]
#[allow(clippy::derive_partial_eq_without_eq)]
#[serde(deny_unknown_fields, crate = "::cosmwasm_schema::serde")]
#[schemars(crate = "::cosmwasm_schema::schemars")]
#[serde(deny_unknown_fields, crate = ":: cosmwasm_schema::serde")]
#[schemars(crate = ":: cosmwasm_schema::schemars")]
#[serde(rename_all = "snake_case")]
pub enum SudoMsg {
StealFunds {
Expand All @@ -129,12 +213,15 @@ mod tests {
#[test]
#[should_panic(expected = "unions are not supported")]
fn unions() {
cw_serde_impl(parse_quote! {
pub union SudoMsg {
x: u32,
y: u32,
}
})
cw_serde_impl(
Options::default(),
parse_quote! {
pub union SudoMsg {
x: u32,
y: u32,
}
},
)
.unwrap();
}
}
53 changes: 45 additions & 8 deletions packages/schema-derive/src/generate_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use syn::{

pub fn write_api_impl(input: Options) -> Block {
let api_object = generate_api_impl(&input);
let crate_name = input.crate_name;
let name = input.name;

parse_quote! {
Expand All @@ -19,7 +20,7 @@ pub fn write_api_impl(input: Options) -> Block {
use ::std::env;
use ::std::fs::{create_dir_all, write};

use ::cosmwasm_schema::{remove_schemas, Api, QueryResponses};
use #crate_name::{remove_schemas, Api, QueryResponses};

let mut out_dir = env::current_dir().unwrap();
out_dir.push("schema");
Expand Down Expand Up @@ -50,6 +51,7 @@ pub fn write_api_impl(input: Options) -> Block {

pub fn generate_api_impl(input: &Options) -> ExprStruct {
let Options {
crate_name,
name,
version,
instantiate,
Expand All @@ -61,7 +63,7 @@ pub fn generate_api_impl(input: &Options) -> ExprStruct {
} = input;

parse_quote! {
::cosmwasm_schema::Api {
#crate_name::Api {
contract_name: #name.to_string(),
contract_version: #version.to_string(),
instantiate: #instantiate,
Expand Down Expand Up @@ -121,6 +123,7 @@ impl Parse for Pair {

#[derive(Debug)]
pub struct Options {
crate_name: TokenStream,
name: TokenStream,
version: TokenStream,
instantiate: TokenStream,
Expand All @@ -136,6 +139,13 @@ impl Parse for Options {
let pairs = input.parse_terminated(Pair::parse, Token![,])?;
let mut map: BTreeMap<_, _> = pairs.into_iter().map(|p| p.0).collect();

let crate_name = if let Some(crate_name_override) = map.remove(&parse_quote!(crate_name)) {
let crate_name_override = crate_name_override.get_type()?;
quote! { #crate_name_override }
} else {
quote! { ::cosmwasm_schema }
};

let name = if let Some(name_override) = map.remove(&parse_quote!(name)) {
let name_override = name_override.get_str()?;
quote! {
Expand All @@ -161,15 +171,15 @@ impl Parse for Options {
let instantiate = match map.remove(&parse_quote!(instantiate)) {
Some(ty) => {
let ty = ty.get_type()?;
quote! {Some(::cosmwasm_schema::schema_for!(#ty))}
quote! {Some(#crate_name::schema_for!(#ty))}
}
None => quote! { None },
};

let execute = match map.remove(&parse_quote!(execute)) {
Some(ty) => {
let ty = ty.get_type()?;
quote! {Some(::cosmwasm_schema::schema_for!(#ty))}
quote! {Some(#crate_name::schema_for!(#ty))}
}
None => quote! { None },
};
Expand All @@ -178,8 +188,8 @@ impl Parse for Options {
Some(ty) => {
let ty = ty.get_type()?;
(
quote! {Some(::cosmwasm_schema::schema_for!(#ty))},
quote! { Some(<#ty as ::cosmwasm_schema::QueryResponses>::response_schemas().unwrap()) },
quote! {Some(#crate_name::schema_for!(#ty))},
quote! { Some(<#ty as #crate_name::QueryResponses>::response_schemas().unwrap()) },
)
}
None => (quote! { None }, quote! { None }),
Expand All @@ -188,15 +198,15 @@ impl Parse for Options {
let migrate = match map.remove(&parse_quote!(migrate)) {
Some(ty) => {
let ty = ty.get_type()?;
quote! {Some(::cosmwasm_schema::schema_for!(#ty))}
quote! {Some(#crate_name::schema_for!(#ty))}
}
None => quote! { None },
};

let sudo = match map.remove(&parse_quote!(sudo)) {
Some(ty) => {
let ty = ty.get_type()?;
quote! {Some(::cosmwasm_schema::schema_for!(#ty))}
quote! {Some(#crate_name::schema_for!(#ty))}
}
None => quote! { None },
};
Expand All @@ -206,6 +216,7 @@ impl Parse for Options {
}

Ok(Self {
crate_name,
name,
version,
instantiate,
Expand All @@ -222,6 +233,32 @@ impl Parse for Options {
mod tests {
use super::*;

#[test]
fn crate_rename() {
assert_eq!(
generate_api_impl(&parse_quote! {
crate_name: ::my_crate::cw_schema,
instantiate: InstantiateMsg,
execute: ExecuteMsg,
query: QueryMsg,
migrate: MigrateMsg,
sudo: SudoMsg,
}),
parse_quote! {
::my_crate::cw_schema::Api {
contract_name: ::std::env!("CARGO_PKG_NAME").to_string(),
contract_version: ::std::env!("CARGO_PKG_VERSION").to_string(),
instantiate: Some(::my_crate::cw_schema::schema_for!(InstantiateMsg)),
execute: Some(::my_crate::cw_schema::schema_for!(ExecuteMsg)),
query: Some(::my_crate::cw_schema::schema_for!(QueryMsg)),
migrate: Some(::my_crate::cw_schema::schema_for!(MigrateMsg)),
sudo: Some(::my_crate::cw_schema::schema_for!(SudoMsg)),
responses: Some(<QueryMsg as ::my_crate::cw_schema::QueryResponses>::response_schemas().unwrap()),
}
}
);
}

#[test]
fn api_object_minimal() {
assert_eq!(
Expand Down
7 changes: 5 additions & 2 deletions packages/schema-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,14 @@ pub fn generate_api(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
fallible_macro! {
#[proc_macro_attribute]
pub fn cw_serde(
_attr: proc_macro::TokenStream,
attr: proc_macro::TokenStream,
input: proc_macro::TokenStream,
) -> syn::Result<proc_macro::TokenStream> {
let options = syn::parse(attr)?;
let input = syn::parse(input)?;
let expanded = cw_serde::cw_serde_impl(input)?;

let expanded = cw_serde::cw_serde_impl(options, input)?;

Ok(expanded.into_token_stream().into())
}
}
Loading

0 comments on commit 66028ce

Please sign in to comment.