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

Distinct handling for N fields + 1 hasher vs N fields + N hashers #458

Merged
merged 11 commits into from
Feb 21, 2022
60 changes: 46 additions & 14 deletions codegen/src/api/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,26 +115,58 @@ fn generate_storage_entry_fns(
(field_name, field_type)
})
.collect::<Vec<_>>();
// toddo: [AJ] use unzip here?
let tuple_struct_fields =
fields.iter().map(|(_, field_type)| field_type);
let field_names = fields.iter().map(|(field_name, _)| field_name);

let field_names = fields.iter().map(|(n,_)| n);
let field_types = fields.iter().map(|(_,t)| t);

let entry_struct = quote! {
pub struct #entry_struct_ident( #( pub #tuple_struct_fields ),* );
pub struct #entry_struct_ident( #( pub #field_types ),* );
};
let constructor =
quote!( #entry_struct_ident( #( #field_names ),* ) );
let keys = (0..tuple.fields().len()).into_iter().zip(hashers).map(
|(field, hasher)| {
let index = syn::Index::from(field);
quote!( ::subxt::StorageMapKey::new(&self.#index, #hasher) )
},
);
let key_impl = quote! {
::subxt::StorageEntryKey::Map(
vec![ #( #keys ),* ]

let key_impl = if hashers.len() == fields.len() {
// If the number of hashers matches the number of fields, we're dealing with a
// StorageNMap, and each field should be hashed separately according to the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment not strictly accurate, as it would be possible (although useless) to have a single tuple key e.g. (KeyType,) for a regular StorageMap

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tweaked the comment to "something shaped like a StorageNMap"; does that make more sense?

I guess in the 1-key version it doesn't really matter what branch we follow as both have the same effect anyway.

// corresponding hasher.
let keys = (0..tuple.fields().len())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd have probably gone with hashers.into_iter().enumerate().take(fields.len()), but that's just how my brain works with iterators, this is fine.

I'd change tuple.fields().len() to fields.len() though.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point; I think I'd have gone for the same sort of thing if I was approaching it fresh :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes that's much more intuitive, I am responsible for this hacky approach 🙈

.into_iter()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think you need this, range should be an iterator already

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I always forget this (because they probably shouldn't be :D)

.zip(hashers)
.map(|(field_idx, hasher)| {
let index = syn::Index::from(field_idx);
quote!( ::subxt::StorageMapKey::new(&self.#index, #hasher) )
});
quote!{
::subxt::StorageEntryKey::Map(
vec![ #( #keys ),* ]
)
}
} else if hashers.len() == 1 {
// If there is one hasher, then however many fields we have, we want to hash a
// tuple of them using the one hasher we're told about. This corresponds to a
// StorageMap.
let hasher = hashers.get(0).expect("checked for 1 hashed");
let items = (0..tuple.fields().len()).into_iter().map(
|field_idx| {
let index = syn::Index::from(field_idx);
quote!( &self.#index )
}
);
quote!{
::subxt::StorageEntryKey::Map(
vec![ ::subxt::StorageMapKey::new(&(#( #items ),*), #hasher) ]
)
}
} else {
// If we hit this condition, we don't know how to handle the number of hashes vs fields
// that we've been handed, so abort.
abort_call_site!(
"Number of hashers ({}) does not equal 1 for StorageMap, or match number of fields ({}) for StorageNMap",
hashers.len(),
fields.len()
)
};

(fields, entry_struct, constructor, key_impl)
}
_ => {
Expand Down