mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-09 19:01:08 +00:00
codegen: Fetch and decode metadata version then fallback (#1092)
* codegen: Fetch and decode metadata version then fallback Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * cli: Add `unstable-metadata` attribute to the subxt macro Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * docs: Add missing comma Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * macro: Add `GenerateRuntimeApi` Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Update subxt/src/lib.rs Co-authored-by: James Wilson <james@jsdw.me> * Update macro/src/lib.rs Co-authored-by: James Wilson <james@jsdw.me> * subxt: Adjust docs Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * cli: Import `GenerateRuntimeApi` Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> --------- Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> Co-authored-by: James Wilson <james@jsdw.me>
This commit is contained in:
@@ -6,7 +6,7 @@ use crate::utils::FileOrUrl;
|
||||
use clap::Parser as ClapParser;
|
||||
use color_eyre::eyre;
|
||||
use color_eyre::eyre::eyre;
|
||||
use subxt_codegen::{DerivesRegistry, TypeSubstitutes, TypeSubstitutionError};
|
||||
use subxt_codegen::{DerivesRegistry, GenerateRuntimeApi, TypeSubstitutes, TypeSubstitutionError};
|
||||
|
||||
/// Generate runtime API client code from metadata.
|
||||
///
|
||||
@@ -185,16 +185,14 @@ fn codegen(
|
||||
}
|
||||
|
||||
let should_gen_docs = !no_docs;
|
||||
let runtime_api = subxt_codegen::generate_runtime_api_from_bytes(
|
||||
item_mod,
|
||||
metadata_bytes,
|
||||
derives,
|
||||
type_substitutes,
|
||||
crate_path,
|
||||
should_gen_docs,
|
||||
runtime_types_only,
|
||||
)
|
||||
.map_err(|code_gen_err| eyre!("{code_gen_err}"))?;
|
||||
|
||||
let runtime_api = GenerateRuntimeApi::new(item_mod, crate_path)
|
||||
.derives_registry(derives)
|
||||
.type_substitutes(type_substitutes)
|
||||
.generate_docs(should_gen_docs)
|
||||
.runtime_types_only(runtime_types_only)
|
||||
.generate_from_bytes(metadata_bytes)
|
||||
.map_err(|code_gen_err| eyre!("{code_gen_err}"))?;
|
||||
writeln!(output, "{runtime_api}")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+144
-94
@@ -28,113 +28,164 @@ use quote::{format_ident, quote};
|
||||
use std::{collections::HashMap, fs, io::Read, path, string::ToString};
|
||||
use syn::parse_quote;
|
||||
|
||||
/// Generates the API for interacting with a Substrate runtime.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `item_mod` - The module declaration for which the API is implemented.
|
||||
/// * `path` - The path to the scale encoded metadata of the runtime node.
|
||||
/// * `derives` - Provide custom derives for the generated types.
|
||||
/// * `type_substitutes` - Provide custom type substitutes.
|
||||
/// * `crate_path` - Path to the `subxt` crate.
|
||||
/// * `should_gen_docs` - True if the generated API contains the documentation from the metadata.
|
||||
/// * `runtime_types_only` - Whether to limit code generation to only runtime types.
|
||||
///
|
||||
/// **Note:** This is a wrapper over [RuntimeGenerator] for static metadata use-cases.
|
||||
pub fn generate_runtime_api_from_path<P>(
|
||||
/// Generate the runtime API for interacting with a substrate runtime.
|
||||
pub struct GenerateRuntimeApi {
|
||||
item_mod: syn::ItemMod,
|
||||
path: P,
|
||||
derives: DerivesRegistry,
|
||||
type_substitutes: TypeSubstitutes,
|
||||
crate_path: CratePath,
|
||||
should_gen_docs: bool,
|
||||
runtime_types_only: bool,
|
||||
) -> Result<TokenStream2, CodegenError>
|
||||
where
|
||||
P: AsRef<path::Path>,
|
||||
{
|
||||
let to_err = |err| CodegenError::Io(path.as_ref().to_string_lossy().into(), err);
|
||||
|
||||
let mut file = fs::File::open(&path).map_err(to_err)?;
|
||||
let mut bytes = Vec::new();
|
||||
file.read_to_end(&mut bytes).map_err(to_err)?;
|
||||
|
||||
generate_runtime_api_from_bytes(
|
||||
item_mod,
|
||||
&bytes,
|
||||
derives,
|
||||
type_substitutes,
|
||||
crate_path,
|
||||
should_gen_docs,
|
||||
runtime_types_only,
|
||||
)
|
||||
unstable_metadata: bool,
|
||||
}
|
||||
|
||||
/// Generates the API for interacting with a substrate runtime, using metadata
|
||||
/// that can be downloaded from a node at the provided URL. This function blocks
|
||||
/// while retrieving the metadata.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `item_mod` - The module declaration for which the API is implemented.
|
||||
/// * `url` - HTTP/WS URL to the substrate node you'd like to pull metadata from.
|
||||
/// * `derives` - Provide custom derives for the generated types.
|
||||
/// * `type_substitutes` - Provide custom type substitutes.
|
||||
/// * `crate_path` - Path to the `subxt` crate.
|
||||
/// * `should_gen_docs` - True if the generated API contains the documentation from the metadata.
|
||||
/// * `runtime_types_only` - Whether to limit code generation to only runtime types.
|
||||
///
|
||||
/// **Note:** This is a wrapper over [RuntimeGenerator] for static metadata use-cases.
|
||||
pub fn generate_runtime_api_from_url(
|
||||
impl GenerateRuntimeApi {
|
||||
/// Construct a new [`GenerateRuntimeApi`].
|
||||
pub fn new(item_mod: syn::ItemMod, crate_path: CratePath) -> Self {
|
||||
GenerateRuntimeApi {
|
||||
item_mod,
|
||||
derives: DerivesRegistry::new(),
|
||||
type_substitutes: TypeSubstitutes::new(),
|
||||
crate_path,
|
||||
should_gen_docs: false,
|
||||
runtime_types_only: false,
|
||||
unstable_metadata: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Provide custom derives for the generated types.
|
||||
///
|
||||
/// Default is no derives.
|
||||
pub fn derives_registry(mut self, derives: DerivesRegistry) -> Self {
|
||||
self.derives = derives;
|
||||
self
|
||||
}
|
||||
|
||||
/// Provide custom type substitutes.
|
||||
///
|
||||
/// Default is no substitutes.
|
||||
pub fn type_substitutes(mut self, type_substitutes: TypeSubstitutes) -> Self {
|
||||
self.type_substitutes = type_substitutes;
|
||||
self
|
||||
}
|
||||
|
||||
/// True if the generated API contains the documentation from the metadata.
|
||||
///
|
||||
/// Default: false.
|
||||
pub fn generate_docs(mut self, should_gen_docs: bool) -> Self {
|
||||
self.should_gen_docs = should_gen_docs;
|
||||
self
|
||||
}
|
||||
|
||||
/// Whether to limit code generation to only runtime types.
|
||||
///
|
||||
/// Default: false.
|
||||
pub fn runtime_types_only(mut self, runtime_types_only: bool) -> Self {
|
||||
self.runtime_types_only = runtime_types_only;
|
||||
self
|
||||
}
|
||||
|
||||
/// Whether to fetch the unstable metadata first.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This takes effect only if the API is generated from URL.
|
||||
///
|
||||
/// Default: false.
|
||||
pub fn unstable_metadata(mut self, unstable_metadata: bool) -> Self {
|
||||
self.unstable_metadata = unstable_metadata;
|
||||
self
|
||||
}
|
||||
|
||||
/// Generate the runtime API from path.
|
||||
pub fn generate_from_path<P>(self, path: P) -> Result<TokenStream2, CodegenError>
|
||||
where
|
||||
P: AsRef<path::Path>,
|
||||
{
|
||||
let to_err = |err| CodegenError::Io(path.as_ref().to_string_lossy().into(), err);
|
||||
|
||||
let mut file = fs::File::open(&path).map_err(to_err)?;
|
||||
let mut bytes = Vec::new();
|
||||
file.read_to_end(&mut bytes).map_err(to_err)?;
|
||||
|
||||
let metadata = Metadata::decode(&mut &bytes[..])?;
|
||||
|
||||
generate_runtime_api_with_metadata(
|
||||
self.item_mod,
|
||||
metadata,
|
||||
self.derives,
|
||||
self.type_substitutes,
|
||||
self.crate_path,
|
||||
self.should_gen_docs,
|
||||
self.runtime_types_only,
|
||||
)
|
||||
}
|
||||
|
||||
/// Generate the runtime API from the provided metadata bytes.
|
||||
pub fn generate_from_bytes(self, bytes: &[u8]) -> Result<TokenStream2, CodegenError> {
|
||||
let metadata = Metadata::decode(&mut &bytes[..])?;
|
||||
|
||||
generate_runtime_api_with_metadata(
|
||||
self.item_mod,
|
||||
metadata,
|
||||
self.derives,
|
||||
self.type_substitutes,
|
||||
self.crate_path,
|
||||
self.should_gen_docs,
|
||||
self.runtime_types_only,
|
||||
)
|
||||
}
|
||||
|
||||
/// Generate the runtime API from URL.
|
||||
///
|
||||
/// The metadata will be downloaded from a node at the provided URL.
|
||||
/// This function blocks while retrieving the metadata.
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// Not recommended to be used in production environments.
|
||||
pub fn generate_from_url(self, url: &Uri) -> Result<TokenStream2, CodegenError> {
|
||||
fn fetch_metadata(url: &Uri, version: MetadataVersion) -> Result<Metadata, CodegenError> {
|
||||
let bytes = fetch_metadata_bytes_blocking(url, version)?;
|
||||
Ok(Metadata::decode(&mut &bytes[..])?)
|
||||
}
|
||||
|
||||
let metadata = self
|
||||
.unstable_metadata
|
||||
.then(|| fetch_metadata(url, MetadataVersion::Unstable).ok())
|
||||
.flatten();
|
||||
|
||||
let metadata = if let Some(unstable) = metadata {
|
||||
unstable
|
||||
} else {
|
||||
match fetch_metadata(url, MetadataVersion::Version(15)) {
|
||||
Ok(metadata) => metadata,
|
||||
Err(_) => fetch_metadata(url, MetadataVersion::Version(14))?,
|
||||
}
|
||||
};
|
||||
|
||||
generate_runtime_api_with_metadata(
|
||||
self.item_mod,
|
||||
metadata,
|
||||
self.derives,
|
||||
self.type_substitutes,
|
||||
self.crate_path,
|
||||
self.should_gen_docs,
|
||||
self.runtime_types_only,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates the API for interacting with a substrate runtime, using the `subxt::Metadata`.
|
||||
fn generate_runtime_api_with_metadata(
|
||||
item_mod: syn::ItemMod,
|
||||
url: &Uri,
|
||||
metadata: Metadata,
|
||||
derives: DerivesRegistry,
|
||||
type_substitutes: TypeSubstitutes,
|
||||
crate_path: CratePath,
|
||||
should_gen_docs: bool,
|
||||
runtime_types_only: bool,
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
// Fetch latest unstable version, if that fails fall back to the latest stable.
|
||||
let bytes = match fetch_metadata_bytes_blocking(url, MetadataVersion::Unstable) {
|
||||
Ok(bytes) => bytes,
|
||||
Err(_) => fetch_metadata_bytes_blocking(url, MetadataVersion::Latest)?,
|
||||
};
|
||||
|
||||
generate_runtime_api_from_bytes(
|
||||
item_mod,
|
||||
&bytes,
|
||||
derives,
|
||||
type_substitutes,
|
||||
crate_path,
|
||||
should_gen_docs,
|
||||
runtime_types_only,
|
||||
)
|
||||
}
|
||||
|
||||
/// Generates the API for interacting with a substrate runtime, using metadata bytes.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `item_mod` - The module declaration for which the API is implemented.
|
||||
/// * `bytes` - The raw metadata bytes.
|
||||
/// * `derives` - Provide custom derives for the generated types.
|
||||
/// * `type_substitutes` - Provide custom type substitutes.
|
||||
/// * `crate_path` - Path to the `subxt` crate.
|
||||
/// * `should_gen_docs` - True if the generated API contains the documentation from the metadata.
|
||||
/// * `runtime_types_only` - Whether to limit code generation to only runtime types.
|
||||
///
|
||||
/// **Note:** This is a wrapper over [RuntimeGenerator] for static metadata use-cases.
|
||||
pub fn generate_runtime_api_from_bytes(
|
||||
item_mod: syn::ItemMod,
|
||||
bytes: &[u8],
|
||||
derives: DerivesRegistry,
|
||||
type_substitutes: TypeSubstitutes,
|
||||
crate_path: CratePath,
|
||||
should_gen_docs: bool,
|
||||
runtime_types_only: bool,
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
let metadata = Metadata::decode(&mut &bytes[..])?;
|
||||
|
||||
let generator = RuntimeGenerator::new(metadata);
|
||||
if runtime_types_only {
|
||||
generator.generate_runtime_types(
|
||||
@@ -164,8 +215,7 @@ impl RuntimeGenerator {
|
||||
/// Create a new runtime generator from the provided metadata.
|
||||
///
|
||||
/// **Note:** If you have the metadata path, URL or bytes to hand, prefer to use
|
||||
/// one of the `generate_runtime_api_from_*` functions for generating the runtime API
|
||||
/// from that.
|
||||
/// `GenerateRuntimeApi` for generating the runtime API from that.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
|
||||
+1
-4
@@ -53,10 +53,7 @@ mod types;
|
||||
pub mod utils;
|
||||
|
||||
pub use self::{
|
||||
api::{
|
||||
generate_runtime_api_from_bytes, generate_runtime_api_from_path,
|
||||
generate_runtime_api_from_url, RuntimeGenerator,
|
||||
},
|
||||
api::{GenerateRuntimeApi, RuntimeGenerator},
|
||||
error::{CodegenError, TypeSubstitutionError},
|
||||
types::{CratePath, Derives, DerivesRegistry, Module, TypeGenerator, TypeSubstitutes},
|
||||
};
|
||||
|
||||
+28
-21
@@ -9,7 +9,9 @@ use std::str::FromStr;
|
||||
use darling::{ast::NestedMeta, FromMeta};
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro_error::{abort_call_site, proc_macro_error};
|
||||
use subxt_codegen::{utils::Uri, CodegenError, DerivesRegistry, TypeSubstitutes};
|
||||
use subxt_codegen::{
|
||||
utils::Uri, CodegenError, DerivesRegistry, GenerateRuntimeApi, TypeSubstitutes,
|
||||
};
|
||||
use syn::{parse_macro_input, punctuated::Punctuated};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -47,6 +49,8 @@ struct RuntimeMetadataArgs {
|
||||
no_default_derives: bool,
|
||||
#[darling(default)]
|
||||
no_default_substitutions: bool,
|
||||
#[darling(default)]
|
||||
unstable_metadata: darling::util::Flag,
|
||||
}
|
||||
|
||||
#[derive(Debug, FromMeta)]
|
||||
@@ -131,36 +135,39 @@ pub fn subxt(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
}
|
||||
|
||||
let should_gen_docs = args.generate_docs.is_present();
|
||||
let unstable_metadata = args.unstable_metadata.is_present();
|
||||
|
||||
let runtime_api_generator = GenerateRuntimeApi::new(item_mod, crate_path)
|
||||
.derives_registry(derives_registry)
|
||||
.type_substitutes(type_substitutes)
|
||||
.generate_docs(should_gen_docs)
|
||||
.runtime_types_only(args.runtime_types_only);
|
||||
|
||||
match (args.runtime_metadata_path, args.runtime_metadata_url) {
|
||||
(Some(rest_of_path), None) => {
|
||||
if unstable_metadata {
|
||||
abort_call_site!(
|
||||
"The 'unstable_metadata' attribute requires `runtime_metadata_url`"
|
||||
)
|
||||
}
|
||||
|
||||
let root = std::env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".into());
|
||||
let root_path = std::path::Path::new(&root);
|
||||
let path = root_path.join(rest_of_path);
|
||||
subxt_codegen::generate_runtime_api_from_path(
|
||||
item_mod,
|
||||
path,
|
||||
derives_registry,
|
||||
type_substitutes,
|
||||
crate_path,
|
||||
should_gen_docs,
|
||||
args.runtime_types_only,
|
||||
)
|
||||
.map_or_else(|err| err.into_compile_error().into(), Into::into)
|
||||
|
||||
runtime_api_generator
|
||||
.generate_from_path(path)
|
||||
.map_or_else(|err| err.into_compile_error().into(), Into::into)
|
||||
}
|
||||
(None, Some(url_string)) => {
|
||||
let url = Uri::from_str(&url_string).unwrap_or_else(|_| {
|
||||
abort_call_site!("Cannot download metadata; invalid url: {}", url_string)
|
||||
});
|
||||
subxt_codegen::generate_runtime_api_from_url(
|
||||
item_mod,
|
||||
&url,
|
||||
derives_registry,
|
||||
type_substitutes,
|
||||
crate_path,
|
||||
should_gen_docs,
|
||||
args.runtime_types_only,
|
||||
)
|
||||
.map_or_else(|err| err.into_compile_error().into(), Into::into)
|
||||
|
||||
runtime_api_generator
|
||||
.unstable_metadata(unstable_metadata)
|
||||
.generate_from_url(&url)
|
||||
.map_or_else(|err| err.into_compile_error().into(), Into::into)
|
||||
}
|
||||
(None, None) => {
|
||||
abort_call_site!(
|
||||
|
||||
@@ -279,6 +279,7 @@ pub mod ext {
|
||||
/// )]
|
||||
/// mod polkadot {}
|
||||
/// ```
|
||||
///
|
||||
/// ## `no_default_derives`
|
||||
///
|
||||
/// By default, the macro will add all derives necessary for the generated code to play nicely with Subxt. Adding this attribute
|
||||
@@ -298,4 +299,19 @@ pub mod ext {
|
||||
/// `scale_decode::DecodeAsType` (because we add `#[codec(..)]` attributes on some fields/types during codegen), and you must use this
|
||||
/// feature in conjunction with `runtime_types_only` (or manually specify a bunch of defaults to make codegen work properly when
|
||||
/// generating the subxt interfaces).
|
||||
///
|
||||
/// ## `unstable_metadata`
|
||||
///
|
||||
/// This attribute works only in combination with `runtime_metadata_url`. By default, the macro will fetch the latest stable
|
||||
/// version of the metadata from the target node. This attribute makes the codegen attempt to fetch the unstable version of
|
||||
/// the metadata first. This is **not recommended** in production code, since the unstable metadata a node is providing is likely
|
||||
/// to be incompatible with Subxt.
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// #[subxt::subxt(
|
||||
/// runtime_metadata_url = "wss://rpc.polkadot.io:443",
|
||||
/// unstable_metadata
|
||||
/// )]
|
||||
/// mod polkadot {}
|
||||
/// ```
|
||||
pub use subxt_macro::subxt;
|
||||
|
||||
Reference in New Issue
Block a user