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())
}
}
}
@@ -0,0 +1,46 @@
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
// This file is part of subxt.
//
// subxt is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// subxt is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
use futures::join;
use sp_keyring::AccountKeyring;
use subxt::{
ClientBuilder,
DefaultConfig,
PolkadotExtrinsicParams,
};
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
pub mod polkadot {}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let api = ClientBuilder::new()
.build()
.await?
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<DefaultConfig>>>();
let addr = AccountKeyring::Bob.to_account_id();
// For storage requests, we can join futures together to
// await multiple futures concurrently:
let a_fut = api.storage().staking().bonded(&addr, None);
let b_fut = api.storage().staking().ledger(&addr, None);
let (a, b) = join!(a_fut, b_fut);
println!("{a:?}, {b:?}");
Ok(())
}