-
Notifications
You must be signed in to change notification settings - Fork 26
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
add NewTypeKey derive macro #88
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
use proc_macro::TokenStream; | ||
use syn::{ | ||
Ident, | ||
__private::{quote::quote, Span}, | ||
parse_macro_input, ItemStruct, | ||
}; | ||
|
||
pub fn index_list(attr: TokenStream, item: TokenStream) -> TokenStream { | ||
let input = parse_macro_input!(item as ItemStruct); | ||
|
||
let ty = Ident::new(&attr.to_string(), Span::call_site()); | ||
let struct_ty = input.ident.clone(); | ||
|
||
let names = input | ||
.fields | ||
.clone() | ||
.into_iter() | ||
.map(|e| { | ||
let name = e.ident.unwrap(); | ||
quote! { &self.#name } | ||
}) | ||
.collect::<Vec<_>>(); | ||
|
||
let expanded = quote! { | ||
#input | ||
|
||
impl cw_storage_plus::IndexList<#ty> for #struct_ty<'_> { | ||
fn get_indexes(&'_ self) -> Box<dyn Iterator<Item = &'_ dyn cw_storage_plus::Index<#ty>> + '_> { | ||
let v: Vec<&dyn cw_storage_plus::Index<#ty>> = vec![#(#names),*]; | ||
Box::new(v.into_iter()) | ||
} | ||
} | ||
}; | ||
|
||
TokenStream::from(expanded) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,43 +2,22 @@ | |
Procedural macros helper for interacting with cw-storage-plus and cosmwasm-storage. | ||
|
||
For more information on this package, please check out the | ||
[README](https://github.com/CosmWasm/cw-plus/blob/main/packages/storage-macro/README.md). | ||
[README](https://github.com/CosmWasm/cw-storage-plus/blob/main/macros/README.md). | ||
*/ | ||
|
||
mod index_list; | ||
mod newtype; | ||
|
||
use proc_macro::TokenStream; | ||
use syn::{ | ||
Ident, | ||
__private::{quote::quote, Span}, | ||
parse_macro_input, ItemStruct, | ||
}; | ||
|
||
// Re-export the procedural macro functions | ||
|
||
#[proc_macro_attribute] | ||
pub fn index_list(attr: TokenStream, item: TokenStream) -> TokenStream { | ||
let input = parse_macro_input!(item as ItemStruct); | ||
|
||
let ty = Ident::new(&attr.to_string(), Span::call_site()); | ||
let struct_ty = input.ident.clone(); | ||
|
||
let names = input | ||
.fields | ||
.clone() | ||
.into_iter() | ||
.map(|e| { | ||
let name = e.ident.unwrap(); | ||
quote! { &self.#name } | ||
}) | ||
.collect::<Vec<_>>(); | ||
|
||
let expanded = quote! { | ||
#input | ||
|
||
impl cw_storage_plus::IndexList<#ty> for #struct_ty<'_> { | ||
fn get_indexes(&'_ self) -> Box<dyn Iterator<Item = &'_ dyn cw_storage_plus::Index<#ty>> + '_> { | ||
let v: Vec<&dyn cw_storage_plus::Index<#ty>> = vec![#(#names),*]; | ||
Box::new(v.into_iter()) | ||
} | ||
} | ||
}; | ||
index_list::index_list(attr, item) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice, thanks for moving this to a separate module |
||
} | ||
|
||
TokenStream::from(expanded) | ||
#[proc_macro_derive(NewTypeKey)] | ||
pub fn cw_storage_newtype_key_derive(input: TokenStream) -> TokenStream { | ||
newtype::cw_storage_newtype_key_derive(input) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
use proc_macro::TokenStream; | ||
use quote::quote; | ||
use syn::{parse_macro_input, Data, DeriveInput, Fields}; | ||
|
||
pub fn cw_storage_newtype_key_derive(input: TokenStream) -> TokenStream { | ||
let input = parse_macro_input!(input as DeriveInput); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if you can parse it directly as ItemStruct (or whatever the type is called in |
||
|
||
let expanded = impl_newtype(&input); | ||
|
||
TokenStream::from(expanded) | ||
} | ||
|
||
fn impl_newtype(input: &DeriveInput) -> proc_macro2::TokenStream { | ||
// Extract the struct name | ||
let name = &input.ident; | ||
|
||
// Extract the inner type | ||
let inner_type = match &input.data { | ||
Data::Struct(data_struct) => match &data_struct.fields { | ||
Fields::Unnamed(fields) if fields.unnamed.len() == 1 => Some(&fields.unnamed[0].ty), | ||
_ => None, | ||
}, | ||
_ => None, | ||
}; | ||
|
||
let inner_type = inner_type | ||
.expect("NewTypeKey can only be derived for newtypes (tuple structs with one field)"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are ways to display macro errors similarly to other rust compiler errors, with source code being highlighted and all. I don't think it's a must, but if you're interested: https://docs.rs/syn/latest/syn/struct.Error.html There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can probably find an example later There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thanks, I don't have a lot of experience writing macros, happy to learn better techniques. Reminding myself to address this and other comments early next week if not later today :) |
||
|
||
// Implement PrimaryKey | ||
let impl_primary_key = quote! { | ||
impl<'a> cw_storage_plus::PrimaryKey<'a> for #name | ||
where | ||
#inner_type: cw_storage_plus::PrimaryKey<'a>, | ||
{ | ||
type Prefix = (); | ||
type SubPrefix = (); | ||
type Suffix = Self; | ||
type SuperSuffix = Self; | ||
|
||
fn key(&self) -> Vec<cw_storage_plus::Key> { | ||
self.0.key() | ||
} | ||
} | ||
}; | ||
|
||
// Implement Prefixer | ||
let impl_prefixer = quote! { | ||
impl<'a> cw_storage_plus::Prefixer<'a> for #name | ||
where | ||
#inner_type: cw_storage_plus::Prefixer<'a>, | ||
{ | ||
fn prefix(&self) -> Vec<cw_storage_plus::Key> { | ||
self.0.prefix() | ||
} | ||
} | ||
}; | ||
|
||
// Implement KeyDeserialize | ||
let impl_key_deserialize = quote! { | ||
impl cw_storage_plus::KeyDeserialize for #name | ||
where | ||
#inner_type: cw_storage_plus::KeyDeserialize<Output = #inner_type>, | ||
{ | ||
type Output = #name; | ||
const KEY_ELEMS: u16 = 1; | ||
|
||
#[inline(always)] | ||
fn from_vec(value: Vec<u8>) -> cosmwasm_std::StdResult<Self::Output> { | ||
<#inner_type as cw_storage_plus::KeyDeserialize>::from_vec(value).map(#name) | ||
} | ||
} | ||
}; | ||
|
||
// Combine all implementations | ||
let expanded = quote! { | ||
#impl_primary_key | ||
#impl_prefixer | ||
#impl_key_deserialize | ||
}; | ||
|
||
expanded | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch!