diff --git a/sube/Cargo.toml b/sube/Cargo.toml index 7aa7f1a..657e79c 100644 --- a/sube/Cargo.toml +++ b/sube/Cargo.toml @@ -52,8 +52,8 @@ libwallet = { path = "../libwallet", default_features=false, features=["substrat rand_core = "0.6.3" [features] -default = ["v14", "dep:heapless"] -test = ["std", "wss", "ws", "http", "json", "v14", "dep:async-std"] +default = ["v14", "dep:heapless", "dep:paste"] +test = ["std", "wss", "ws", "http", "json", "v14", "dep:async-std", "dep:rand_core"] http = ["dep:jsonrpc", "surf/h1-client-rustls"] http-web = ["dep:jsonrpc", "dep:wasm-bindgen", "surf/wasm-client"] json = ["scales/json"] @@ -63,7 +63,7 @@ v14 = ["dep:scale-info", "frame-metadata/current"] ws = ["dep:async-mutex", "dep:async-std", "dep:ewebsock", "dep:futures-channel", "dep:futures-util", "dep:jsonrpc", "async-std/unstable"] wss = ["dep:async-tls", "ws", "ewebsock/tls", "async-std/unstable"] examples = ["dep:rand_core"] -js = ["http-web", "json", "v14", 'async-std/unstable', "ws"] +js = ["http-web", "json", "v14", 'async-std/unstable', "ws", "dep:rand_core"] [package.metadata.docs.rs] features = ["http"] diff --git a/sube/examples/pallet_communities.rs b/sube/examples/pallet_communities.rs index a6c84a0..d6cfb4f 100644 --- a/sube/examples/pallet_communities.rs +++ b/sube/examples/pallet_communities.rs @@ -10,7 +10,7 @@ async fn main() -> Result<()> { let response = sube!("ws://127.0.0.1:12281/communityMemberships/collection").await?; - if let Response::ValueSet(value) = result { + if let Response::ValueSet(value) = response { let data = serde_json::to_value(&value).expect("to be serializable"); println!("Collection {}", serde_json::to_string_pretty(&data).expect("it must return an str")); } diff --git a/sube/examples/send_tx.rs b/sube/examples/send_tx.rs deleted file mode 100644 index 1025446..0000000 --- a/sube/examples/send_tx.rs +++ /dev/null @@ -1,42 +0,0 @@ -use futures_util::TryFutureExt; -use libwallet::{self, vault, Account, Signature}; -use rand_core::OsRng; -use serde_json::json; -use std::env; -use sube::{sube, Error}; - -type Wallet = libwallet::Wallet>; - -#[async_std::main] -async fn main() -> Result<(), Box> { - let phrase = env::args().skip(1).collect::>().join(" "); - - let (vault, phrase) = if phrase.is_empty() { - vault::Simple::generate_with_phrase(&mut rand_core::OsRng) - } else { - let phrase: libwallet::Mnemonic = phrase.parse().expect("Invalid phrase"); - (vault::Simple::from_phrase(&phrase), phrase) - }; - - let mut wallet = Wallet::new(vault); - wallet.unlock(None, None).await?; - - let account = wallet.default_account(); - let public = account.unwrap().public(); - - let response = sube!("wss://rococo-rpc.polkadot.io/balances/transfer" => { - body: json!({ - "dest": { "Id": public.as_ref() }, - "value": 100000 - }), - signer: (public, |message: &[u8]| async { - Ok::<_, Error>(wallet.sign(message).await.map_err(|_| Error::Signing)?) - }), - }) - .await?; - - println!("Secret phrase: \"{phrase}\""); - println!("Default Account: 0x{}", account.unwrap()); - - Ok(()) -} diff --git a/sube/examples/send_tx_builder.rs b/sube/examples/send_tx_builder.rs index 31fcce9..375058f 100644 --- a/sube/examples/send_tx_builder.rs +++ b/sube/examples/send_tx_builder.rs @@ -2,13 +2,13 @@ use jsonrpc::error; use libwallet::{self, vault, Account, Signature}; use rand_core::OsRng; use serde_json::json; -use std::{env, error::Error}; -use sube::SubeBuilder; +use std::{env}; +use sube::{SubeBuilder, Signer, SignerFn, Error}; type Wallet = libwallet::Wallet>; #[async_std::main] -async fn main() -> Result<(), Box> { +async fn main() -> Result<(), Error> { let phrase = env::args().skip(1).collect::>().join(" "); let (vault, phrase) = if phrase.is_empty() { @@ -19,10 +19,10 @@ async fn main() -> Result<(), Box> { }; let mut wallet = Wallet::new(vault); - wallet.unlock(None, None).await?; + wallet.unlock(None, None).await.expect("unlocked"); let account = wallet.default_account().unwrap(); - + let response = SubeBuilder::default() .with_url("wss://rococo-rpc.polkadot.io/balances/transfer") .with_body(json!({ @@ -34,13 +34,14 @@ async fn main() -> Result<(), Box> { .with_signer(sube::SignerFn::from(( account.public().as_ref(), |message: &[u8]| async { - Ok(wallet + Ok::<_, Error>(wallet .sign(message) .await .map_err(|_| sube::Error::Signing)?) }, ))) - .await?; + .await + .expect("it must work"); Ok(()) } diff --git a/sube/examples/send_tx_libwallet.rs b/sube/examples/send_tx_libwallet.rs index 61d6a4f..5c7d1db 100644 --- a/sube/examples/send_tx_libwallet.rs +++ b/sube/examples/send_tx_libwallet.rs @@ -32,7 +32,7 @@ async fn main() -> Result<(), Box> { "value": 100000 })) ) - .await?; + .await.expect("it must to work"); println!("Secret phrase: \"{phrase}\""); // println!("Default Account: 0x{account}"); diff --git a/sube/src/builder.rs b/sube/src/builder.rs index 1d2879c..c8e3055 100644 --- a/sube/src/builder.rs +++ b/sube/src/builder.rs @@ -8,17 +8,20 @@ use crate::{ }; use crate::{prelude::*, Offline, StorageChangeSet}; +use alloc::rc::Rc; +use alloc::sync::Arc; use async_trait::async_trait; + +use async_mutex::Mutex; +use core::borrow::Borrow; use core::{ - cell::OnceCell, future::{Future, IntoFuture}, marker::PhantomData, }; -// use heapless::Vec as HVec; -use url::Url; -// type PairHostBackend<'a> = (&'a str, AnyBackend, Metadata); -// static INSTANCE: OnceCell> = OnceCell::new(); +// use std::os::macos::raw::stat; + +use url::Url; pub struct SubeBuilder<'a, Body, Signer> { url: Option<&'a str>, @@ -85,6 +88,8 @@ impl<'a> SubeBuilder<'a, (), ()> { }) .await?; + // let (backend, meta) = get_cache_by_url(url.clone(), metadata).await?; + Ok(match path { "_meta" => Response::Meta(meta), "_meta/registry" => Response::Registry(&meta.types), @@ -131,18 +136,20 @@ where let path = url.path(); let body = body.ok_or(Error::BadInput)?; - let backend = BACKEND - .get_or_try_init(get_backend_by_url(url.clone())) - .await?; + let (backend, meta) = get_cache_by_url(url.clone(), metadata).await?; - let meta = META - .get_or_try_init(async { - match metadata { - Some(m) => Ok(m), - None => backend.metadata().await.map_err(|err| Error::BadMetadata), - } - }) - .await?; + // let backend = BACKEND + // .get_or_try_init(get_backend_by_url(url.clone())) + // .await?; + + // let meta = META + // .get_or_try_init(async { + // match metadata { + // Some(m) => Ok(m), + // None => backend.metadata().await.map_err(|_| Error::BadMetadata), + // } + // }) + // .await?; Ok(match path { "_meta" => Response::Meta(meta), @@ -156,6 +163,58 @@ where } } +#[inline] +async fn get_cache_by_url<'a>( + url: Url, + metadata: Option, +) -> SubeResult<(&'a AnyBackend, &'a Metadata)> { + let mut instance_backend = INSTANCE_BACKEND + .get_or_init(async { Mutex::new(Map::new()) }) + .await + .lock() + .await; + + let mut instance_metadata = INSTANCE_METADATA + .get_or_init(async { Mutex::new(Map::new()) }) + .await + .lock() + .await; + + let base_path = format!( + "{}://{}", + url.scheme(), + url.host_str().expect("url to have a host") + ); + + let cached_b = instance_backend.get(&base_path); + let cached_m = instance_metadata.get(&base_path); + + match (cached_b, cached_m) { + (Some(b), Some(m)) => Ok((b, m)), + _ => { + let backend = Box::new(get_backend_by_url(url.clone()).await?); + let backend = Box::leak::<'static>(backend); + + instance_backend.insert(base_path.clone(), backend); + + let metadata = Box::new(get_metadata(backend, metadata).await?); + let metadata = Box::leak::<'static>(metadata); + + instance_metadata.insert(base_path.clone(), metadata); + + Ok((backend, metadata)) + } + } +} + +use heapless::FnvIndexMap as Map; + +static INSTANCE_BACKEND: async_once_cell::OnceCell>> = + async_once_cell::OnceCell::new(); + +static INSTANCE_METADATA: async_once_cell::OnceCell>> = + async_once_cell::OnceCell::new(); + static BACKEND: async_once_cell::OnceCell = async_once_cell::OnceCell::new(); static META: async_once_cell::OnceCell = async_once_cell::OnceCell::new(); @@ -236,7 +295,7 @@ impl Backend for &AnyBackend { async fn query_storage_at( &self, keys: Vec, - block: Option + block: Option, ) -> crate::Result> { match self { #[cfg(any(feature = "http", feature = "http-web"))] @@ -320,24 +379,6 @@ macro_rules! sube { } }; - // Two parameters - // Match when the macro is called with an expression (url) followed by a block of key-value pairs - ( $url:expr => { $($key:ident: $value:expr),+ $(,)? }) => { - - async { - use $crate::paste; - - let mut builder = $crate::SubeBuilder::default() - .with_url($url); - - paste!($( - let mut builder = builder.[]($value); - )*); - - builder.await - } - }; - ($url:expr => ($wallet:expr, $body:expr)) => { async { let mut builder = $crate::SubeBuilder::default(); diff --git a/sube/src/lib.rs b/sube/src/lib.rs index d7a5759..7c4f34d 100644 --- a/sube/src/lib.rs +++ b/sube/src/lib.rs @@ -27,6 +27,7 @@ use codec::Encode; pub use core::fmt::Display; pub use frame_metadata::RuntimeMetadataPrefixed; pub use signer::Signer; +pub use paste::paste; use core::future::Future; pub use meta::Metadata;