Skip to content

Commit

Permalink
Filter out fields with #[codec(skip)] (#16)
Browse files Browse the repository at this point in the history
* filter out fields with #[codec(skip)]

* apply skipping to unnamed too, tidy and add tests

* use darling to mirror scale-decode

* Better docs on skip fn

Co-authored-by: Niklas Adolfsson <[email protected]>

* Update scale-encode-derive/src/lib.rs

---------

Co-authored-by: Michael Calvert <[email protected]>
Co-authored-by: Niklas Adolfsson <[email protected]>
  • Loading branch information
3 people authored Aug 1, 2023
1 parent 2697f92 commit 6fd1d9d
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 9 deletions.
43 changes: 34 additions & 9 deletions scale-encode-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use darling::FromAttributes;
use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote};
use syn::{parse_macro_input, punctuated::Punctuated, DeriveInput};

// The default attribute name for attrs
const ATTR_NAME: &str = "encode_as_type";

// Macro docs in main crate; don't add any docs here.
#[proc_macro_derive(EncodeAsType, attributes(encode_as_type))]
#[proc_macro_derive(EncodeAsType, attributes(encode_as_type, codec))]
pub fn derive_macro(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);

Expand Down Expand Up @@ -76,6 +78,7 @@ fn generate_enum_impl(

quote!(
impl #impl_generics #path_to_scale_encode::EncodeAsType for #path_to_type #ty_generics #where_clause {
#[allow(unused_variables)]
fn encode_as_type_to(
&self,
// long variable names to prevent conflict with struct field names:
Expand Down Expand Up @@ -108,6 +111,7 @@ fn generate_struct_impl(

quote!(
impl #impl_generics #path_to_scale_encode::EncodeAsType for #path_to_type #ty_generics #where_clause {
#[allow(unused_variables)]
fn encode_as_type_to(
&self,
// long variable names to prevent conflict with struct field names:
Expand All @@ -124,6 +128,7 @@ fn generate_struct_impl(
}
}
impl #impl_generics #path_to_scale_encode::EncodeAsFields for #path_to_type #ty_generics #where_clause {
#[allow(unused_variables)]
fn encode_as_fields_to(
&self,
// long variable names to prevent conflict with struct field names:
Expand Down Expand Up @@ -181,11 +186,14 @@ fn fields_to_matcher_and_composite(
let field_name = &f.ident;
quote!(#field_name)
});
let tuple_body = fields.named.iter().map(|f| {
let field_name_str = f.ident.as_ref().unwrap().to_string();
let field_name = &f.ident;
quote!((Some(#field_name_str), #field_name as &dyn #path_to_scale_encode::EncodeAsType))
});
let tuple_body = fields.named
.iter()
.filter(|f| !should_skip(&f.attrs))
.map(|f| {
let field_name_str = f.ident.as_ref().unwrap().to_string();
let field_name = &f.ident;
quote!((Some(#field_name_str), #field_name as &dyn #path_to_scale_encode::EncodeAsType))
});

(
quote!({#( #match_body ),*}),
Expand All @@ -197,10 +205,12 @@ fn fields_to_matcher_and_composite(
.unnamed
.iter()
.enumerate()
.map(|(idx, _)| format_ident!("_{idx}"));
let match_body = field_idents.clone().map(|i| quote!(#i));
.map(|(idx, f)| (format_ident!("_{idx}"), f));

let match_body = field_idents.clone().map(|(i, _)| quote!(#i));
let tuple_body = field_idents
.map(|i| quote!((None as Option<&'static str>, #i as &dyn #path_to_scale_encode::EncodeAsType)));
.filter(|(_, f)| !should_skip(&f.attrs))
.map(|(i, _)| quote!((None as Option<&'static str>, #i as &dyn #path_to_scale_encode::EncodeAsType)));

(
quote!((#( #match_body ),*)),
Expand Down Expand Up @@ -255,3 +265,18 @@ impl TopLevelAttrs {
Ok(res)
}
}

// Checks if the attributes contain `skip`.
//
// NOTE: Since we only care about `skip` at the moment, we just expose this helper,
// but if we add more attrs we can expose `FieldAttrs` properly:
fn should_skip(attrs: &[syn::Attribute]) -> bool {
#[derive(FromAttributes, Default)]
#[darling(attributes(encode_as_type, codec))]
struct FieldAttrs {
#[darling(default)]
skip: bool,
}

FieldAttrs::from_attributes(attrs).unwrap_or_default().skip
}
74 changes: 74 additions & 0 deletions scale-encode/src/impls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1079,4 +1079,78 @@ mod test {
("hello".to_string(), (123u8,), true, (1u64,)),
);
}

#[test]
fn encode_to_number_skipping_attrs_via_macro_works() {
struct NotEncodeAsType;

#[derive(EncodeAsType)]
#[encode_as_type(crate_path = "crate")]
struct FooNotSkipping {
value: u64,
other: bool,
third: String,
}

#[derive(EncodeAsType)]
#[encode_as_type(crate_path = "crate")]
struct FooSkipping {
value: u64,
#[encode_as_type(skip)]
other: bool,
// Even though this type doesn't impl EncodeAsType,
// it's ignored so should be fine:
#[codec(skip)]
third: NotEncodeAsType,
}

assert_value_roundtrips_to(
FooSkipping {
value: 123,
other: true,
third: NotEncodeAsType,
},
123u64,
);
}

#[test]
fn encode_unnamed_to_number_skipping_attrs_via_macro_works() {
struct NotEncodeAsType;

#[derive(EncodeAsType)]
#[encode_as_type(crate_path = "crate")]
struct FooSkipping(
u64,
#[encode_as_type(skip)] bool,
// Even though this type doesn't impl EncodeAsType,
// it's ignored so should be fine:
#[codec(skip)] NotEncodeAsType,
);

assert_value_roundtrips_to(FooSkipping(123, true, NotEncodeAsType), 123u64);
}

// If you don't skip values, you can't turn a multi-value
// struct into a number.
#[test]
#[should_panic]
fn encode_to_number_not_skipping_via_macro_fails() {
#[derive(EncodeAsType)]
#[encode_as_type(crate_path = "crate")]
struct FooNotSkipping {
value: u64,
other: bool,
third: String,
}

assert_value_roundtrips_to(
FooNotSkipping {
value: 123,
other: true,
third: "hello".to_string(),
},
123u64,
);
}
}

0 comments on commit 6fd1d9d

Please sign in to comment.