diff --git a/Cargo.lock b/Cargo.lock index a77d87d0d0..187e79ea9d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10591,6 +10591,7 @@ dependencies = [ "subxt", "subxt-codegen", "subxt-metadata", + "subxt-utils-fetchmetadata", "syn 2.0.77", "thiserror", "tokio", @@ -10603,8 +10604,6 @@ dependencies = [ "frame-metadata 17.0.0", "getrandom", "heck 0.5.0", - "hex", - "jsonrpsee", "parity-scale-codec", "proc-macro2", "quote", @@ -10613,7 +10612,6 @@ dependencies = [ "subxt-metadata", "syn 2.0.77", "thiserror", - "tokio", ] [[package]] @@ -10683,6 +10681,7 @@ dependencies = [ "quote", "scale-typegen", "subxt-codegen", + "subxt-utils-fetchmetadata", "syn 2.0.77", ] @@ -10738,6 +10737,19 @@ dependencies = [ "syn 2.0.77", ] +[[package]] +name = "subxt-utils-fetchmetadata" +version = "0.37.0" +dependencies = [ + "frame-metadata 17.0.0", + "hex", + "jsonrpsee", + "parity-scale-codec", + "thiserror", + "tokio", + "url", +] + [[package]] name = "syn" version = "1.0.109" diff --git a/Cargo.toml b/Cargo.toml index ebdd50a23c..28749d86c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ members = [ "signer", "subxt", "scripts/artifacts", + "utils/fetch-metadata", ] # We exclude any crates that would depend on non mutually @@ -140,6 +141,7 @@ polkadot-sdk = { version = "0.7", default-features = false } # Subxt workspace crates: subxt = { version = "0.37.0", path = "subxt", default-features = false } subxt-core = { version = "0.37.0", path = "core", default-features = false } +subxt-utils-fetchmetadata = { version = "0.37.0", path = "utils/fetch-metadata", default-features = false } subxt-macro = { version = "0.37.0", path = "macro" } subxt-metadata = { version = "0.37.0", path = "metadata", default-features = false } subxt-codegen = { version = "0.37.0", path = "codegen" } diff --git a/cli/Cargo.toml b/cli/Cargo.toml index d6392da894..e980a8b3ea 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -26,9 +26,10 @@ workspace = true chain-spec-pruning = ["smoldot"] [dependencies] -subxt-codegen = { workspace = true, features = ["fetch-metadata"] } +subxt-codegen = { workspace = true } +subxt-utils-fetchmetadata = { workspace = true, features = ["url"] } subxt-metadata = { workspace = true } -subxt = { workspace = true, features = ["native", "jsonrpsee"] } +subxt = { workspace = true, features = ["default"] } clap = { workspace = true } serde = { workspace = true, features = ["derive"] } color-eyre = { workspace = true } diff --git a/cli/src/commands/chain_spec/fetch.rs b/cli/src/commands/chain_spec/fetch.rs index b151b5bcd4..98cb7a6d85 100644 --- a/cli/src/commands/chain_spec/fetch.rs +++ b/cli/src/commands/chain_spec/fetch.rs @@ -9,7 +9,7 @@ use jsonrpsee::{ http_client::HttpClientBuilder, }; use std::time::Duration; -use subxt_codegen::fetch_metadata::Url; +use subxt_utils_fetchmetadata::Url; /// Returns the node's chainSpec from the provided URL. pub async fn fetch_chain_spec(url: Url) -> Result { diff --git a/cli/src/commands/chain_spec/mod.rs b/cli/src/commands/chain_spec/mod.rs index f0d2928acc..3157327e69 100644 --- a/cli/src/commands/chain_spec/mod.rs +++ b/cli/src/commands/chain_spec/mod.rs @@ -6,7 +6,7 @@ use clap::Parser as ClapParser; #[cfg(feature = "chain-spec-pruning")] use serde_json::Value; use std::{io::Write, path::PathBuf}; -use subxt_codegen::fetch_metadata::Url; +use subxt_utils_fetchmetadata::Url; mod fetch; diff --git a/cli/src/commands/compatibility.rs b/cli/src/commands/compatibility.rs index 890c47dd1f..349f6f6e47 100644 --- a/cli/src/commands/compatibility.rs +++ b/cli/src/commands/compatibility.rs @@ -8,8 +8,8 @@ use color_eyre::eyre::WrapErr; use jsonrpsee::client_transport::ws::Url; use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use subxt_codegen::fetch_metadata::MetadataVersion; use subxt_metadata::Metadata; +use subxt_utils_fetchmetadata::MetadataVersion; use crate::utils::validate_url_security; @@ -137,7 +137,7 @@ async fn fetch_runtime_metadata( url: Url, version: MetadataVersion, ) -> color_eyre::Result { - let bytes = subxt_codegen::fetch_metadata::fetch_metadata_from_url(url, version).await?; + let bytes = subxt_utils_fetchmetadata::from_url(url, version).await?; let metadata = Metadata::decode(&mut &bytes[..])?; Ok(metadata) } diff --git a/cli/src/utils.rs b/cli/src/utils.rs index 624d5256ae..a1f7a8c975 100644 --- a/cli/src/utils.rs +++ b/cli/src/utils.rs @@ -14,7 +14,7 @@ use std::{fs, io::Read, path::PathBuf}; use subxt::{OnlineClient, PolkadotConfig}; use scale_value::Value; -use subxt_codegen::fetch_metadata::{fetch_metadata_from_url, MetadataVersion, Url}; +use subxt_utils_fetchmetadata::{self as fetch_metadata, MetadataVersion, Url}; /// The source of the metadata. #[derive(Debug, Args, Clone)] @@ -117,12 +117,12 @@ impl FileOrUrl { } // Fetch from --url (None, Some(uri), version) => { - Ok(fetch_metadata_from_url(uri.clone(), version.unwrap_or_default()).await?) + Ok(fetch_metadata::from_url(uri.clone(), version.unwrap_or_default()).await?) } // Default if neither is provided; fetch from local url (None, None, version) => { let url = Url::parse("ws://localhost:9944").expect("Valid URL; qed"); - Ok(fetch_metadata_from_url(url, version.unwrap_or_default()).await?) + Ok(fetch_metadata::from_url(url, version.unwrap_or_default()).await?) } } } diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml index 14f21a1a19..ffb348d266 100644 --- a/codegen/Cargo.toml +++ b/codegen/Cargo.toml @@ -13,8 +13,7 @@ description = "Generate an API for interacting with a substrate node from FRAME [features] default = [] -fetch-metadata = ["dep:jsonrpsee", "dep:tokio", "dep:frame-metadata"] -web = ["jsonrpsee?/async-wasm-client", "jsonrpsee?/client-web-transport", "getrandom/js"] +web = ["getrandom/js"] [dependencies] codec = { package = "parity-scale-codec", workspace = true, features = ["derive"] } @@ -25,9 +24,6 @@ quote = { workspace = true } syn = { workspace = true } scale-info = { workspace = true } subxt-metadata = { workspace = true } -jsonrpsee = { workspace = true, features = ["async-client", "client-ws-transport-tls", "http-client"], optional = true } -hex = { workspace = true, features = ["std"] } -tokio = { workspace = true, features = ["rt-multi-thread"], optional = true } thiserror = { workspace = true } scale-typegen = { workspace = true } @@ -36,9 +32,10 @@ getrandom = { workspace = true, optional = true } [dev-dependencies] scale-info = { workspace = true, features = ["bit-vec"] } +frame-metadata = { workspace = true } [package.metadata.docs.rs] -features = ["fetch-metadata"] +features = ["default"] rustdoc-args = ["--cfg", "docsrs"] [package.metadata.playground] diff --git a/codegen/src/error.rs b/codegen/src/error.rs index 93091b59d6..c6c7f77dea 100644 --- a/codegen/src/error.rs +++ b/codegen/src/error.rs @@ -11,9 +11,6 @@ use scale_typegen::TypegenError; #[derive(Debug, thiserror::Error)] #[non_exhaustive] pub enum CodegenError { - /// Cannot fetch the metadata bytes. - #[error("Failed to fetch metadata, make sure that you're pointing at a node which is providing substrate-based metadata: {0}")] - Fetch(#[from] FetchMetadataError), /// Cannot decode the metadata bytes. #[error("Could not decode metadata, only V14 and V15 metadata are supported: {0}")] Decode(#[from] codec::Error), @@ -60,6 +57,9 @@ pub enum CodegenError { /// Error when generating metadata from Wasm-runtime #[error("Failed to generate metadata from wasm file. reason: {0}")] Wasm(String), + /// Other error. + #[error("Other error: {0}")] + Other(String), } impl CodegenError { @@ -81,28 +81,3 @@ impl CodegenError { syn::Error::new(span, msg).into_compile_error() } } - -/// Error attempting to load metadata. -#[derive(Debug, thiserror::Error)] -#[non_exhaustive] -pub enum FetchMetadataError { - /// Error decoding from a hex value. - #[error("Cannot decode hex value: {0}")] - DecodeError(#[from] hex::FromHexError), - /// Some SCALE codec error. - #[error("Cannot scale encode/decode value: {0}")] - CodecError(#[from] codec::Error), - /// JSON-RPC error fetching metadata. - #[cfg(feature = "fetch-metadata")] - #[error("Request error: {0}")] - RequestError(#[from] jsonrpsee::core::ClientError), - /// Failed IO when fetching from a file. - #[error("Failed IO for {0}, make sure that you are providing the correct file path for metadata: {1}")] - Io(String, std::io::Error), - /// URL scheme is not http, https, ws or wss. - #[error("'{0}' not supported, supported URI schemes are http, https, ws or wss.")] - InvalidScheme(String), - /// Some other error. - #[error("Other error: {0}")] - Other(String), -} diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index c61b060400..9f9ec62e98 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -6,20 +6,13 @@ //! This is used by the `#[subxt]` macro and `subxt codegen` CLI command, but can also //! be used directly if preferable. -#![deny(unused_crate_dependencies, missing_docs)] +#![deny(missing_docs)] #![cfg_attr(docsrs, feature(doc_cfg))] mod api; pub mod error; mod ir; -// These should probably be in a separate crate; they are used by the -// macro and CLI tool, so they only live here because this is a common -// crate that both depend on. -#[cfg(feature = "fetch-metadata")] -#[cfg_attr(docsrs, doc(cfg(feature = "fetch-metadata")))] -pub mod fetch_metadata; - #[cfg(feature = "web")] use getrandom as _; diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 2902b680ce..f1b549782b 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -15,7 +15,8 @@ description = "Generate types and helpers for interacting with Substrate runtime [features] web = ["subxt-codegen/web"] -runtime-path = ["polkadot-sdk"] +runtime-metadata-path = ["polkadot-sdk"] +runtime-metadata-insecure-url = ["subxt-utils-fetchmetadata/url"] [lib] proc-macro = true @@ -26,7 +27,8 @@ darling = { workspace = true } proc-macro-error2 = { workspace = true } syn = { workspace = true } quote = { workspace = true } -subxt-codegen = { workspace = true, features = ["fetch-metadata"] } +subxt-codegen = { workspace = true } +subxt-utils-fetchmetadata = { workspace = true } scale-typegen = { workspace = true } polkadot-sdk = { workspace = true, optional = true, features = ["sp-io", "sc-executor-common", "sp-state-machine", "sp-maybe-compressed-blob", "sc-executor"] } diff --git a/macro/src/lib.rs b/macro/src/lib.rs index 0828dc4919..cf54893190 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -13,15 +13,10 @@ use scale_typegen::typegen::{ settings::substitutes::path_segments, validation::{registry_contains_type_path, similar_type_paths_in_registry}, }; -use subxt_codegen::{ - fetch_metadata::{ - fetch_metadata_from_file_blocking, fetch_metadata_from_url_blocking, MetadataVersion, Url, - }, - CodegenBuilder, CodegenError, Metadata, -}; +use subxt_codegen::{CodegenBuilder, CodegenError, Metadata}; use syn::{parse_macro_input, punctuated::Punctuated}; -#[cfg(feature = "runtime-path")] +#[cfg(feature = "runtime-metadata-path")] mod wasm_loader; #[derive(Clone, Debug)] @@ -61,7 +56,7 @@ struct RuntimeMetadataArgs { no_default_substitutions: bool, #[darling(default)] unstable_metadata: darling::util::Flag, - #[cfg(feature = "runtime-path")] + #[cfg(feature = "runtime-metadata-path")] #[darling(default)] runtime_path: Option, } @@ -211,7 +206,7 @@ fn fetch_metadata(args: &RuntimeMetadataArgs) -> Result Result { + use subxt_utils_fetchmetadata::{from_url_blocking, MetadataVersion, Url}; + let url = Url::parse(url_string).unwrap_or_else(|_| { abort_call_site!("Cannot download metadata; invalid url: {}", url_string) }); @@ -254,30 +252,36 @@ fn fetch_metadata(args: &RuntimeMetadataArgs) -> Result MetadataVersion::Latest, }; - fetch_metadata_from_url_blocking(url, version) - .map_err(CodegenError::from) + from_url_blocking(url, version) + .map_err(|e| CodegenError::Other(e.to_string())) .and_then(|b| subxt_codegen::Metadata::decode(&mut &*b).map_err(Into::into)) .map_err(|e| e.into_compile_error())? } - #[cfg(feature = "runtime-path")] + #[cfg(not(feature = "runtime-metadata-insecure-url"))] + (None, Some(_)) => { + abort_call_site!( + "'runtime_metadata_insecure_url' requires the 'runtime-metadata-insecure-url' feature to be enabled" + ) + } + #[cfg(feature = "runtime-metadata-path")] (None, None) => { abort_call_site!( "At least one of 'runtime_metadata_path', 'runtime_metadata_insecure_url' or 'runtime_path` can be provided" ) } - #[cfg(not(feature = "runtime-path"))] + #[cfg(not(feature = "runtime-metadata-path"))] (None, None) => { abort_call_site!( "At least one of 'runtime_metadata_path', 'runtime_metadata_insecure_url' can be provided" ) } - #[cfg(feature = "runtime-path")] + #[cfg(feature = "runtime-metadata-path")] _ => { abort_call_site!( "Only one of 'runtime_metadata_path', 'runtime_metadata_insecure_url' or 'runtime_path` can be provided" ) } - #[cfg(not(feature = "runtime-path"))] + #[cfg(not(feature = "runtime-metadata-path"))] _ => { abort_call_site!( "Only one of 'runtime_metadata_path' or 'runtime_metadata_insecure_url' can be provided" diff --git a/macro/src/wasm_loader.rs b/macro/src/wasm_loader.rs index 60393f9a58..f8be106dce 100644 --- a/macro/src/wasm_loader.rs +++ b/macro/src/wasm_loader.rs @@ -12,15 +12,15 @@ use polkadot_sdk::{ sp_maybe_compressed_blob::{self, CODE_BLOB_BOMB_LIMIT}, sp_state_machine, }; -use subxt_codegen::{fetch_metadata::fetch_metadata_from_file_blocking, CodegenError, Metadata}; +use subxt_codegen::{CodegenError, Metadata}; /// Result type shorthand pub type WasmMetadataResult = Result; /// Uses wasm artifact produced by compiling the runtime to generate metadata pub fn from_wasm_file(wasm_file_path: &Path) -> WasmMetadataResult { - let wasm_file = fetch_metadata_from_file_blocking(wasm_file_path) - .map_err(Into::::into) + let wasm_file = subxt_utils_fetchmetadata::from_file_blocking(wasm_file_path) + .map_err(|e| CodegenError::Other(e.to_string())) .and_then(maybe_decompress)?; call_and_decode(wasm_file) } diff --git a/subxt/Cargo.toml b/subxt/Cargo.toml index 949cbb813f..e8fab06d7e 100644 --- a/subxt/Cargo.toml +++ b/subxt/Cargo.toml @@ -60,7 +60,7 @@ reconnecting-rpc-client = ["dep:finito", "jsonrpsee"] # Enable this to use jsonrpsee (allowing for example `OnlineClient::from_url`). jsonrpsee = [ "dep:jsonrpsee", - "runtime", + "runtime" ] # Enable this to pull in extra Substrate dependencies which make it possible to @@ -79,7 +79,7 @@ unstable-metadata = [] unstable-light-client = ["subxt-lightclient"] # Activate this to expose the ability to generate metadata from Wasm runtime files. -runtime-path = ["subxt-macro/runtime-path"] +runtime-metadata-path = ["subxt-macro/runtime-metadata-path"] [dependencies] async-trait = { workspace = true } diff --git a/testing/ui-tests/Cargo.toml b/testing/ui-tests/Cargo.toml index e3fce0041b..58641779ab 100644 --- a/testing/ui-tests/Cargo.toml +++ b/testing/ui-tests/Cargo.toml @@ -14,6 +14,6 @@ hex = { workspace = true } scale-info = { workspace = true, features = ["bit-vec"] } frame-metadata = { workspace = true } codec = { package = "parity-scale-codec", workspace = true, features = ["derive", "bit-vec"] } -subxt = { workspace = true, features = ["native", "jsonrpsee", "runtime-path"] } +subxt = { workspace = true, features = ["native", "jsonrpsee", "runtime-metadata-path"] } subxt-metadata = { workspace = true } generate-custom-metadata = { path = "../generate-custom-metadata" } diff --git a/utils/fetch-metadata/Cargo.toml b/utils/fetch-metadata/Cargo.toml new file mode 100644 index 0000000000..c79e61e95b --- /dev/null +++ b/utils/fetch-metadata/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "subxt-utils-fetchmetadata" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +publish = true +autotests = false + +license.workspace = true +repository.workspace = true +documentation.workspace = true +homepage.workspace = true +description = "subxt utils fetch metadata" + +[features] +url = ["dep:jsonrpsee", "dep:tokio", "dep:url", "frame-metadata"] + +[dependencies] +thiserror = { workspace = true } +codec = { package = "parity-scale-codec", workspace = true, features = ["derive", "std"] } +hex = { workspace = true, features = ["std"] } + +# Optional dependencies for the `url` feature. +jsonrpsee = { workspace = true, features = ["ws-client", "http-client"], optional = true } +tokio = { workspace = true, features = ["rt-multi-thread"], optional = true } +url = { workspace = true, optional = true } +frame-metadata = { workspace = true, optional = true, features = ["std"] } + +[package.metadata.docs.rs] +features = ["url"] +rustdoc-args = ["--cfg", "docsrs"] + +[package.metadata.playground] +default-features = true + +[lints] +workspace = true \ No newline at end of file diff --git a/utils/fetch-metadata/src/error.rs b/utils/fetch-metadata/src/error.rs new file mode 100644 index 0000000000..6c657acb65 --- /dev/null +++ b/utils/fetch-metadata/src/error.rs @@ -0,0 +1,28 @@ +// Copyright 2019-2024 Parity Technologies (UK) Ltd. +// This file is dual-licensed as Apache-2.0 or GPL-3.0. +// see LICENSE for license details. + +/// Error attempting to fetch metadata. +#[derive(Debug, thiserror::Error)] +#[non_exhaustive] +pub enum Error { + /// Error decoding from a hex value. + #[error("Cannot decode hex value: {0}")] + DecodeError(#[from] hex::FromHexError), + /// Some SCALE codec error. + #[error("Cannot scale encode/decode value: {0}")] + CodecError(#[from] codec::Error), + /// JSON-RPC error fetching metadata. + #[cfg(feature = "url")] + #[error("Request error: {0}")] + RequestError(#[from] jsonrpsee::core::ClientError), + /// Failed IO when fetching from a file. + #[error("Failed IO for {0}, make sure that you are providing the correct file path for metadata: {1}")] + Io(String, std::io::Error), + /// URL scheme is not http, https, ws or wss. + #[error("'{0}' not supported, supported URI schemes are http, https, ws or wss.")] + InvalidScheme(String), + /// Some other error. + #[error("Other error: {0}")] + Other(String), +} diff --git a/utils/fetch-metadata/src/lib.rs b/utils/fetch-metadata/src/lib.rs new file mode 100644 index 0000000000..b906f99360 --- /dev/null +++ b/utils/fetch-metadata/src/lib.rs @@ -0,0 +1,28 @@ +// Copyright 2019-2024 Parity Technologies (UK) Ltd. +// This file is dual-licensed as Apache-2.0 or GPL-3.0. +// see LICENSE for license details. + +//! Subxt utils fetch metadata. + +// Internal helper macros +#[macro_use] +mod macros; +mod error; + +cfg_fetch_from_url! { + mod url; + pub use url::{from_url, from_url_blocking, MetadataVersion, Url}; +} + +pub use error::Error; + +/// Fetch metadata from a file in a blocking manner. +pub fn from_file_blocking(path: &std::path::Path) -> Result, error::Error> { + use std::io::Read; + + let to_err = |err| error::Error::Io(path.to_string_lossy().into(), err); + let mut file = std::fs::File::open(path).map_err(to_err)?; + let mut bytes = Vec::new(); + file.read_to_end(&mut bytes).map_err(to_err)?; + Ok(bytes) +} diff --git a/utils/fetch-metadata/src/macros.rs b/utils/fetch-metadata/src/macros.rs new file mode 100644 index 0000000000..4274331afa --- /dev/null +++ b/utils/fetch-metadata/src/macros.rs @@ -0,0 +1,22 @@ +// Copyright 2019-2024 Parity Technologies (UK) Ltd. +// This file is dual-licensed as Apache-2.0 or GPL-3.0. +// see LICENSE for license details. + +macro_rules! cfg_feature { + ($feature:literal, $($item:item)*) => { + $( + #[cfg(feature = $feature)] + #[cfg_attr(docsrs, doc(cfg(feature = $feature)))] + $item + )* + } +} + +macro_rules! cfg_fetch_from_url { + ($($item:item)*) => { + crate::macros::cfg_feature!("url", $($item)*); + }; +} + +#[allow(unused)] +pub(crate) use {cfg_feature, cfg_fetch_from_url}; diff --git a/codegen/src/fetch_metadata.rs b/utils/fetch-metadata/src/url.rs similarity index 69% rename from codegen/src/fetch_metadata.rs rename to utils/fetch-metadata/src/url.rs index ee7c554013..adb2accf5c 100644 --- a/codegen/src/fetch_metadata.rs +++ b/utils/fetch-metadata/src/url.rs @@ -1,21 +1,16 @@ -// Copyright 2019-2023 Parity Technologies (UK) Ltd. +// Copyright 2019-2024 Parity Technologies (UK) Ltd. // This file is dual-licensed as Apache-2.0 or GPL-3.0. // see LICENSE for license details. -//! Helper methods for fetching metadata from a file or URL. +//! Fetch metadata from a URL. -use crate::error::FetchMetadataError; +use crate::Error; use codec::{Decode, Encode}; use jsonrpsee::{ - async_client::ClientBuilder, - client_transport::ws::WsTransportClientBuilder, - core::client::{ClientT, Error}, - http_client::HttpClientBuilder, - rpc_params, + core::client::ClientT, http_client::HttpClientBuilder, rpc_params, ws_client::WsClientBuilder, }; -use std::time::Duration; -pub use jsonrpsee::client_transport::ws::Url; +pub use url::Url; /// The metadata version that is fetched from the node. #[derive(Default, Debug, Clone, Copy)] @@ -48,24 +43,20 @@ impl std::str::FromStr for MetadataVersion { } } -/// Fetch metadata from a file. -pub fn fetch_metadata_from_file_blocking( - path: &std::path::Path, -) -> Result, FetchMetadataError> { - use std::io::Read; - let to_err = |err| FetchMetadataError::Io(path.to_string_lossy().into(), err); - let mut file = std::fs::File::open(path).map_err(to_err)?; - let mut bytes = Vec::new(); - file.read_to_end(&mut bytes).map_err(to_err)?; +/// Returns the metadata bytes from the provided URL. +pub async fn from_url(url: Url, version: MetadataVersion) -> Result, Error> { + let bytes = match url.scheme() { + "http" | "https" => fetch_metadata_http(url, version).await, + "ws" | "wss" => fetch_metadata_ws(url, version).await, + invalid_scheme => Err(Error::InvalidScheme(invalid_scheme.to_owned())), + }?; + Ok(bytes) } /// Returns the metadata bytes from the provided URL, blocking the current thread. -pub fn fetch_metadata_from_url_blocking( - url: Url, - version: MetadataVersion, -) -> Result, FetchMetadataError> { - tokio_block_on(fetch_metadata_from_url(url, version)) +pub fn from_url_blocking(url: Url, version: MetadataVersion) -> Result, Error> { + tokio_block_on(from_url(url, version)) } // Block on some tokio runtime for sync contexts @@ -77,60 +68,33 @@ fn tokio_block_on>(fut: Fut) -> T { .block_on(fut) } -/// Returns the metadata bytes from the provided URL. -pub async fn fetch_metadata_from_url( - url: Url, - version: MetadataVersion, -) -> Result, FetchMetadataError> { - let bytes = match url.scheme() { - "http" | "https" => fetch_metadata_http(url, version).await, - "ws" | "wss" => fetch_metadata_ws(url, version).await, - invalid_scheme => Err(FetchMetadataError::InvalidScheme(invalid_scheme.to_owned())), - }?; - - Ok(bytes) -} - -async fn fetch_metadata_ws( - url: Url, - version: MetadataVersion, -) -> Result, FetchMetadataError> { - let (sender, receiver) = WsTransportClientBuilder::default() - .build(url) - .await - .map_err(|e| Error::Transport(e.into()))?; - - let client = ClientBuilder::default() - .request_timeout(Duration::from_secs(180)) +async fn fetch_metadata_ws(url: Url, version: MetadataVersion) -> Result, Error> { + let client = WsClientBuilder::default() + .request_timeout(std::time::Duration::from_secs(180)) .max_buffer_capacity_per_subscription(4096) - .build_with_tokio(sender, receiver); + .build(url) + .await?; fetch_metadata(client, version).await } -async fn fetch_metadata_http( - url: Url, - version: MetadataVersion, -) -> Result, FetchMetadataError> { +async fn fetch_metadata_http(url: Url, version: MetadataVersion) -> Result, Error> { let client = HttpClientBuilder::default() - .request_timeout(Duration::from_secs(180)) + .request_timeout(std::time::Duration::from_secs(180)) .build(url)?; fetch_metadata(client, version).await } /// The innermost call to fetch metadata: -async fn fetch_metadata( - client: impl ClientT, - version: MetadataVersion, -) -> Result, FetchMetadataError> { +async fn fetch_metadata(client: impl ClientT, version: MetadataVersion) -> Result, Error> { const UNSTABLE_METADATA_VERSION: u32 = u32::MAX; // Fetch metadata using the "new" state_call interface async fn fetch_inner( client: &impl ClientT, version: MetadataVersion, - ) -> Result, FetchMetadataError> { + ) -> Result, Error> { // Look up supported versions: let supported_versions: Vec = { let res: String = client @@ -149,14 +113,12 @@ async fn fetch_metadata( .iter() .filter(|&&v| v != UNSTABLE_METADATA_VERSION) .max() - .ok_or_else(|| { - FetchMetadataError::Other("No valid metadata versions returned".to_string()) - })?, + .ok_or_else(|| Error::Other("No valid metadata versions returned".to_string()))?, MetadataVersion::Unstable => { if supported_versions.contains(&UNSTABLE_METADATA_VERSION) { UNSTABLE_METADATA_VERSION } else { - return Err(FetchMetadataError::Other( + return Err(Error::Other( "The node does not have an unstable metadata version available".to_string(), )); } @@ -165,7 +127,7 @@ async fn fetch_metadata( if supported_versions.contains(&version) { version } else { - return Err(FetchMetadataError::Other(format!( + return Err(Error::Other(format!( "The node does not have version {version} available" ))); } @@ -187,7 +149,7 @@ async fn fetch_metadata( let metadata: Option = Decode::decode(&mut &metadata_bytes[..])?; let Some(metadata) = metadata else { - return Err(FetchMetadataError::Other(format!( + return Err(Error::Other(format!( "The node does not have version {version} available" ))); }; @@ -198,13 +160,13 @@ async fn fetch_metadata( async fn fetch_inner_legacy( client: &impl ClientT, version: MetadataVersion, - ) -> Result, FetchMetadataError> { + ) -> Result, Error> { // If the user specifically asks for anything other than version 14 or "latest", error. if !matches!( version, MetadataVersion::Latest | MetadataVersion::Version(14) ) { - return Err(FetchMetadataError::Other( + return Err(Error::Other( "The node can only return version 14 metadata using the legacy API but you've asked for something else" .to_string(), ));