Make storage futures only borrow client, not self, for better ergonomics (#561)

* Allow storage requests to be made concurrently

* fmt and license

* Update codegen/src/api/storage.rs

Capitalise comment

Co-authored-by: Tarik Gul <47201679+TarikGul@users.noreply.github.com>

* Update codegen/src/api/storage.rs

Capitalise comment

Co-authored-by: Tarik Gul <47201679+TarikGul@users.noreply.github.com>

* split should_ref things up to move them closer to where they are used

* fmt + clippy

Co-authored-by: Tarik Gul <47201679+TarikGul@users.noreply.github.com>
This commit is contained in:
James Wilson
2022-06-14 10:59:09 +01:00
committed by GitHub
parent 972af4ff8d
commit 7ba95c2739
2 changed files with 105 additions and 30 deletions
+59 -30
View File
@@ -240,12 +240,6 @@ fn generate_storage_entry_fns(
}
};
let (lifetime_param, reference, anon_lifetime) = if should_ref {
(quote!(<'a>), quote!(&), quote!(<'_>))
} else {
(quote!(), quote!(), quote!())
};
let storage_entry_impl = quote! (
const PALLET: &'static str = #pallet_name;
const STORAGE: &'static str = #storage_name;
@@ -255,6 +249,10 @@ fn generate_storage_entry_fns(
}
);
let anon_lifetime = match should_ref {
true => quote!(<'_>),
false => quote!(),
};
let storage_entry_type = quote! {
#entry_struct
impl ::subxt::StorageEntry for #entry_struct_ident #anon_lifetime {
@@ -264,22 +262,38 @@ fn generate_storage_entry_fns(
let docs = &storage_entry.docs;
let docs_token = quote! { #( #[doc = #docs ] )* };
let lifetime_param = match should_ref {
true => quote!(<'a>),
false => quote!(),
};
let client_iter_fn = if matches!(storage_entry.ty, StorageEntryType::Map { .. }) {
quote! (
#docs_token
pub async fn #fn_name_iter(
pub fn #fn_name_iter(
&self,
block_hash: ::core::option::Option<T::Hash>,
) -> ::core::result::Result<::subxt::KeyIter<'a, T, #entry_struct_ident #lifetime_param>, ::subxt::BasicError> {
let runtime_storage_hash = {
let locked_metadata = self.client.metadata();
let metadata = locked_metadata.read();
metadata.storage_hash::<#entry_struct_ident>()?
};
if runtime_storage_hash == [#(#storage_hash,)*] {
self.client.storage().iter(block_hash).await
} else {
Err(::subxt::MetadataError::IncompatibleMetadata.into())
) -> impl ::core::future::Future<
Output = ::core::result::Result<::subxt::KeyIter<'a, T, #entry_struct_ident #lifetime_param>, ::subxt::BasicError>
> + 'a {
// Instead of an async fn which borrows all of self,
// we make sure that the returned future only borrows
// client, which allows you to chain calls a little better.
let client = self.client;
async move {
let runtime_storage_hash = {
let locked_metadata = client.metadata();
let metadata = locked_metadata.read();
match metadata.storage_hash::<#entry_struct_ident>() {
Ok(hash) => hash,
Err(e) => return Err(e.into())
}
};
if runtime_storage_hash == [#(#storage_hash,)*] {
client.storage().iter(block_hash).await
} else {
Err(::subxt::MetadataError::IncompatibleMetadata.into())
}
}
}
)
@@ -287,6 +301,10 @@ fn generate_storage_entry_fns(
quote!()
};
let key_args_ref = match should_ref {
true => quote!(&'a),
false => quote!(),
};
let key_args = fields.iter().map(|(field_name, field_type)| {
// The field type is translated from `std::vec::Vec<T>` to `[T]`, if the
// interface should generate a reference. In such cases, the vector ultimately is
@@ -295,26 +313,37 @@ fn generate_storage_entry_fns(
Some(ty) if should_ref => quote!([#ty]),
_ => quote!(#field_type),
};
quote!( #field_name: #reference #field_ty )
quote!( #field_name: #key_args_ref #field_ty )
});
let client_fns = quote! {
#docs_token
pub async fn #fn_name(
pub fn #fn_name(
&self,
#( #key_args, )*
block_hash: ::core::option::Option<T::Hash>,
) -> ::core::result::Result<#return_ty, ::subxt::BasicError> {
let runtime_storage_hash = {
let locked_metadata = self.client.metadata();
let metadata = locked_metadata.read();
metadata.storage_hash::<#entry_struct_ident>()?
};
if runtime_storage_hash == [#(#storage_hash,)*] {
let entry = #constructor;
self.client.storage().#fetch(&entry, block_hash).await
} else {
Err(::subxt::MetadataError::IncompatibleMetadata.into())
) -> impl ::core::future::Future<
Output = ::core::result::Result<#return_ty, ::subxt::BasicError>
> + 'a {
// Instead of an async fn which borrows all of self,
// we make sure that the returned future only borrows
// client, which allows you to chain calls a little better.
let client = self.client;
async move {
let runtime_storage_hash = {
let locked_metadata = client.metadata();
let metadata = locked_metadata.read();
match metadata.storage_hash::<#entry_struct_ident>() {
Ok(hash) => hash,
Err(e) => return Err(e.into())
}
};
if runtime_storage_hash == [#(#storage_hash,)*] {
let entry = #constructor;
client.storage().#fetch(&entry, block_hash).await
} else {
Err(::subxt::MetadataError::IncompatibleMetadata.into())
}
}
}