Tidy subxt-codegen crate interface (#1225)

* first pass tidying codegen crate interface

* fix a codegen test

* macro: keep error spans

* clippy

* fix doc example

* removecommented-out code
This commit is contained in:
James Wilson
2023-10-27 16:35:18 +01:00
committed by GitHub
parent 98170c8fdb
commit 2b21f8dc8c
29 changed files with 503 additions and 554 deletions
Generated
+1
View File
@@ -4312,6 +4312,7 @@ name = "subxt-macro"
version = "0.32.1"
dependencies = [
"darling 0.20.3",
"parity-scale-codec",
"proc-macro-error",
"subxt-codegen",
"syn 2.0.37",
+1 -1
View File
@@ -17,7 +17,7 @@ name = "subxt"
path = "src/main.rs"
[dependencies]
subxt-codegen = { workspace = true }
subxt-codegen = { workspace = true, features = ["fetch-metadata"] }
subxt-metadata = { workspace = true }
subxt = { workspace = true, features = ["native", "jsonrpsee"] }
clap = { workspace = true }
+60 -50
View File
@@ -4,9 +4,9 @@
use crate::utils::FileOrUrl;
use clap::Parser as ClapParser;
use color_eyre::eyre;
use codec::Decode;
use color_eyre::eyre::eyre;
use subxt_codegen::{DerivesRegistry, GenerateRuntimeApi, TypeSubstitutes, TypeSubstitutionError};
use subxt_codegen::CodegenBuilder;
/// Generate runtime API client code from metadata.
///
@@ -132,67 +132,77 @@ fn codegen(
no_default_substitutions: bool,
output: &mut impl std::io::Write,
) -> color_eyre::Result<()> {
let item_mod = syn::parse_quote!(
pub mod api {}
);
let mut codegen = CodegenBuilder::new();
let universal_derives = raw_derives
// Use the provided crate path:
if let Some(crate_path) = crate_path {
let crate_path =
syn::parse_str(&crate_path).map_err(|e| eyre!("Cannot parse crate path: {e}"))?;
codegen.set_subxt_crate_path(crate_path);
}
// Respect the boolean flags:
if runtime_types_only {
codegen.runtime_types_only()
}
if no_default_derives {
codegen.disable_default_derives()
}
if no_default_substitutions {
codegen.disable_default_substitutes()
}
if no_docs {
codegen.no_docs()
}
// Configure derives:
let global_derives = raw_derives
.iter()
.map(|raw| syn::parse_str(raw))
.collect::<Result<Vec<_>, _>>()?;
.collect::<Result<Vec<_>, _>>()
.map_err(|e| eyre!("Cannot parse global derives: {e}"))?;
codegen.set_additional_global_derives(global_derives);
for (ty_str, derive) in derives_for_type {
let ty: syn::TypePath = syn::parse_str(&ty_str)
.map_err(|e| eyre!("Cannot parse derive for type {ty_str}: {e}"))?;
let derive = syn::parse_str(&derive)
.map_err(|e| eyre!("Cannot parse derive for type {ty_str}: {e}"))?;
codegen.add_derives_for_type(ty, std::iter::once(derive));
}
// Configure attribtues:
let universal_attributes = raw_attributes
.iter()
.map(|raw| syn::parse_str(raw))
.map(|attr: syn::Result<OuterAttribute>| attr.map(|attr| attr.0))
.collect::<Result<Vec<_>, _>>()?;
.collect::<Result<Vec<_>, _>>()
.map_err(|e| eyre!("Cannot parse global attributes: {e}"))?;
codegen.set_additional_global_attributes(universal_attributes);
let crate_path = crate_path.map(Into::into).unwrap_or_default();
let mut derives = if no_default_derives {
DerivesRegistry::new()
} else {
DerivesRegistry::with_default_derives(&crate_path)
};
derives.extend_for_all(universal_derives, universal_attributes);
for (ty, derive) in derives_for_type {
let ty = syn::parse_str(&ty)?;
let derive = syn::parse_str(&derive)?;
derives.extend_for_type(ty, std::iter::once(derive), vec![]);
}
for (ty, attr) in attributes_for_type {
let ty = syn::parse_str(&ty)?;
let attribute: OuterAttribute = syn::parse_str(&attr)?;
derives.extend_for_type(ty, vec![], std::iter::once(attribute.0));
for (ty_str, attr) in attributes_for_type {
let ty = syn::parse_str(&ty_str)
.map_err(|e| eyre!("Cannot parse attribute for type {ty_str}: {e}"))?;
let attribute: OuterAttribute = syn::parse_str(&attr)
.map_err(|e| eyre!("Cannot parse attribute for type {ty_str}: {e}"))?;
codegen.add_attributes_for_type(ty, std::iter::once(attribute.0));
}
let mut type_substitutes = if no_default_substitutions {
TypeSubstitutes::new()
} else {
TypeSubstitutes::with_default_substitutes(&crate_path)
};
// Insert type substitutions:
for (from_str, to_str) in substitute_types {
let from: syn::Path = syn::parse_str(&from_str)?;
let to: syn::Path = syn::parse_str(&to_str)?;
let to = to.try_into().map_err(|e: TypeSubstitutionError| {
eyre::eyre!("Cannot parse substitute '{from_str}={to_str}': {e}")
})?;
type_substitutes
.insert(from, to)
.map_err(|e: TypeSubstitutionError| {
eyre::eyre!("Cannot parse substitute '{from_str}={to_str}': {e}")
})?;
let from: syn::Path = syn::parse_str(&from_str)
.map_err(|e| eyre!("Cannot parse type substitution for path {from_str}: {e}"))?;
let to: syn::Path = syn::parse_str(&to_str)
.map_err(|e| eyre!("Cannot parse type substitution for path {from_str}: {e}"))?;
codegen.set_type_substitute(from, to);
}
let should_gen_docs = !no_docs;
let metadata = subxt_metadata::Metadata::decode(&mut &*metadata_bytes)
.map_err(|e| eyre!("Cannot decode the provided metadata: {e}"))?;
let code = codegen
.generate(metadata)
.map_err(|e| eyre!("Cannot generate code: {e}"))?;
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}")?;
writeln!(output, "{code}")?;
Ok(())
}
+2 -2
View File
@@ -8,7 +8,7 @@ use color_eyre::eyre::WrapErr;
use jsonrpsee::client_transport::ws::Url;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use subxt_codegen::utils::MetadataVersion;
use subxt_codegen::fetch_metadata::MetadataVersion;
use subxt_metadata::Metadata;
/// Verify metadata compatibility between substrate nodes.
@@ -128,7 +128,7 @@ async fn fetch_runtime_metadata(
url: Url,
version: MetadataVersion,
) -> color_eyre::Result<Metadata> {
let bytes = subxt_codegen::utils::fetch_metadata_bytes(url, version).await?;
let bytes = subxt_codegen::fetch_metadata::fetch_metadata_from_url(url, version).await?;
let metadata = Metadata::decode(&mut &bytes[..])?;
Ok(metadata)
}
+5 -10
View File
@@ -8,7 +8,7 @@ use color_eyre::eyre;
use std::str::FromStr;
use std::{fs, io::Read, path::PathBuf};
use subxt_codegen::utils::{MetadataVersion, Url};
use subxt_codegen::fetch_metadata::{fetch_metadata_from_url, MetadataVersion, Url};
pub mod type_description;
pub mod type_example;
@@ -57,18 +57,13 @@ impl FileOrUrl {
eyre::bail!("`--file` is incompatible with `--version`")
}
// Fetch from --url
(None, Some(uri), version) => Ok(subxt_codegen::utils::fetch_metadata_bytes(
uri.clone(),
version.unwrap_or_default(),
)
.await?),
(None, Some(uri), version) => {
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(
subxt_codegen::utils::fetch_metadata_bytes(url, version.unwrap_or_default())
.await?,
)
Ok(fetch_metadata_from_url(url, version.unwrap_or_default()).await?)
}
}
}
+1 -1
View File
@@ -13,7 +13,7 @@ homepage.workspace = true
description = "Generate an API for interacting with a substrate node from FRAME metadata"
[features]
default = ["fetch-metadata"]
default = []
fetch-metadata = ["dep:jsonrpsee", "dep:tokio", "dep:frame-metadata"]
web = ["jsonrpsee?/async-wasm-client", "jsonrpsee?/client-web-transport", "getrandom/js"]
+2 -5
View File
@@ -3,10 +3,7 @@
// see LICENSE for license details.
use super::CodegenError;
use crate::{
types::{CompositeDefFields, TypeGenerator},
CratePath,
};
use crate::types::{CompositeDefFields, TypeGenerator};
use heck::{ToSnakeCase as _, ToUpperCamelCase as _};
use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote};
@@ -25,7 +22,7 @@ pub fn generate_calls(
type_gen: &TypeGenerator,
pallet: &PalletMetadata,
types_mod_ident: &syn::Ident,
crate_path: &CratePath,
crate_path: &syn::Path,
should_gen_docs: bool,
) -> Result<TokenStream2, CodegenError> {
// Early return if the pallet has no calls.
+2 -2
View File
@@ -2,7 +2,7 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use crate::{types::TypeGenerator, CratePath};
use crate::types::TypeGenerator;
use heck::ToSnakeCase as _;
use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote};
@@ -37,7 +37,7 @@ pub fn generate_constants(
type_gen: &TypeGenerator,
pallet: &PalletMetadata,
types_mod_ident: &syn::Ident,
crate_path: &CratePath,
crate_path: &syn::Path,
should_gen_docs: bool,
) -> Result<TokenStream2, CodegenError> {
// Early return if the pallet has no constants.
+6 -6
View File
@@ -4,7 +4,7 @@
use std::collections::HashSet;
use crate::{types::TypeGenerator, CratePath};
use crate::types::TypeGenerator;
use heck::ToSnakeCase as _;
use subxt_metadata::{CustomValueMetadata, Metadata};
@@ -12,10 +12,10 @@ use proc_macro2::TokenStream as TokenStream2;
use quote::{quote, ToTokens};
/// Generate the custom values mod, if there are any custom values in the metadata. Else returns None.
pub fn generate_custom_values<'a>(
metadata: &'a Metadata,
type_gen: &'a TypeGenerator,
crate_path: &'a CratePath,
pub fn generate_custom_values(
metadata: &Metadata,
type_gen: &TypeGenerator,
crate_path: &syn::Path,
) -> TokenStream2 {
let mut fn_names_taken = HashSet::new();
let custom = metadata.custom();
@@ -37,7 +37,7 @@ pub fn generate_custom_values<'a>(
fn generate_custom_value_fn(
custom_value: CustomValueMetadata,
type_gen: &TypeGenerator,
crate_path: &CratePath,
crate_path: &syn::Path,
fn_names_taken: &mut HashSet<String>,
) -> Option<TokenStream2> {
// names are transformed to snake case to make for good function identifiers.
+2 -2
View File
@@ -2,7 +2,7 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use crate::{types::TypeGenerator, CratePath};
use crate::types::TypeGenerator;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use subxt_metadata::PalletMetadata;
@@ -42,7 +42,7 @@ pub fn generate_events(
type_gen: &TypeGenerator,
pallet: &PalletMetadata,
types_mod_ident: &syn::Ident,
crate_path: &CratePath,
crate_path: &syn::Path,
should_gen_docs: bool,
) -> Result<TokenStream2, CodegenError> {
// Early return if the pallet has no events.
+4 -188
View File
@@ -14,203 +14,19 @@ mod storage;
use subxt_metadata::Metadata;
use super::DerivesRegistry;
use crate::api::custom_values::generate_custom_values;
use crate::error::CodegenError;
use crate::types::DerivesRegistry;
use crate::{
ir,
types::{CompositeDef, CompositeDefFields, TypeGenerator, TypeSubstitutes},
CratePath,
};
use codec::Decode;
use heck::ToSnakeCase as _;
use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote};
use std::{fs, io::Read, path, string::ToString};
use syn::parse_quote;
/// Generate the runtime API for interacting with a substrate runtime.
pub struct GenerateRuntimeApi {
item_mod: syn::ItemMod,
derives: DerivesRegistry,
type_substitutes: TypeSubstitutes,
crate_path: CratePath,
should_gen_docs: bool,
runtime_types_only: bool,
unstable_metadata: bool,
}
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.
#[cfg(feature = "fetch-metadata")]
pub fn generate_from_url(self, url: crate::utils::Url) -> Result<TokenStream2, CodegenError> {
use crate::utils::{fetch_metadata_bytes_blocking, MetadataVersion, Url};
fn fetch_metadata(url: Url, 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.clone(), MetadataVersion::Unstable).ok())
.flatten();
let metadata = if let Some(unstable) = metadata {
unstable
} else {
match fetch_metadata(url.clone(), 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,
metadata: Metadata,
derives: DerivesRegistry,
type_substitutes: TypeSubstitutes,
crate_path: CratePath,
should_gen_docs: bool,
runtime_types_only: bool,
) -> Result<TokenStream2, CodegenError> {
let generator = RuntimeGenerator::new(metadata);
if runtime_types_only {
generator.generate_runtime_types(
item_mod,
derives,
type_substitutes,
crate_path,
should_gen_docs,
)
} else {
generator.generate_runtime(
item_mod,
derives,
type_substitutes,
crate_path,
should_gen_docs,
)
}
}
/// Create the API for interacting with a Substrate runtime.
pub struct RuntimeGenerator {
metadata: Metadata,
@@ -246,7 +62,7 @@ impl RuntimeGenerator {
item_mod: syn::ItemMod,
derives: DerivesRegistry,
type_substitutes: TypeSubstitutes,
crate_path: CratePath,
crate_path: syn::Path,
should_gen_docs: bool,
) -> Result<TokenStream2, CodegenError> {
let item_mod_attrs = item_mod.attrs.clone();
@@ -300,7 +116,7 @@ impl RuntimeGenerator {
item_mod: syn::ItemMod,
derives: DerivesRegistry,
type_substitutes: TypeSubstitutes,
crate_path: CratePath,
crate_path: syn::Path,
should_gen_docs: bool,
) -> Result<TokenStream2, CodegenError> {
let item_mod_attrs = item_mod.attrs.clone();
@@ -545,7 +361,7 @@ pub fn generate_structs_from_variants<F>(
type_id: u32,
variant_to_struct_name: F,
error_message_type_name: &str,
crate_path: &CratePath,
crate_path: &syn::Path,
should_gen_docs: bool,
) -> Result<Vec<(String, CompositeDef)>, CodegenError>
where
+3 -3
View File
@@ -2,7 +2,7 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use crate::{types::TypeGenerator, CodegenError, CratePath};
use crate::{types::TypeGenerator, CodegenError};
use heck::ToSnakeCase as _;
use heck::ToUpperCamelCase as _;
use subxt_metadata::{Metadata, RuntimeApiMetadata};
@@ -15,7 +15,7 @@ fn generate_runtime_api(
api: RuntimeApiMetadata,
type_gen: &TypeGenerator,
types_mod_ident: &syn::Ident,
crate_path: &CratePath,
crate_path: &syn::Path,
should_gen_docs: bool,
) -> Result<(TokenStream2, TokenStream2), CodegenError> {
// Trait name must remain as is (upper case) to identity the runtime call.
@@ -130,7 +130,7 @@ pub fn generate_runtime_apis(
metadata: &Metadata,
type_gen: &TypeGenerator,
types_mod_ident: &syn::Ident,
crate_path: &CratePath,
crate_path: &syn::Path,
should_gen_docs: bool,
) -> Result<TokenStream2, CodegenError> {
let runtime_fns: Vec<_> = metadata
+4 -4
View File
@@ -2,8 +2,8 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use crate::types::TypeGenerator;
use crate::types::TypePath;
use crate::{types::TypeGenerator, CratePath};
use heck::ToSnakeCase as _;
use proc_macro2::{Ident, TokenStream as TokenStream2, TokenStream};
use quote::{format_ident, quote};
@@ -27,7 +27,7 @@ pub fn generate_storage(
type_gen: &TypeGenerator,
pallet: &PalletMetadata,
types_mod_ident: &syn::Ident,
crate_path: &CratePath,
crate_path: &syn::Path,
should_gen_docs: bool,
) -> Result<TokenStream2, CodegenError> {
let Some(storage) = pallet.storage() else {
@@ -59,7 +59,7 @@ fn generate_storage_entry_fns(
type_gen: &TypeGenerator,
pallet: &PalletMetadata,
storage_entry: &StorageEntryMetadata,
crate_path: &CratePath,
crate_path: &syn::Path,
should_gen_docs: bool,
) -> Result<TokenStream2, CodegenError> {
let keys: Vec<(Ident, TypePath)> = match storage_entry.entry_type() {
@@ -269,7 +269,7 @@ mod tests {
item_mod,
Default::default(),
Default::default(),
"::subxt_path".into(),
syn::parse_str("::subxt_path").unwrap(),
false,
)
.expect("should be able to generate runtime");
+8 -3
View File
@@ -16,9 +16,6 @@ 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),
/// Failed IO for the metadata file.
#[error("Failed IO for {0}, make sure that you are providing the correct file path for metadata: {1}")]
Io(String, std::io::Error),
/// Cannot decode the metadata bytes.
#[error("Could not decode metadata, only V14 and V15 metadata are supported: {0}")]
Decode(#[from] codec::Error),
@@ -85,15 +82,23 @@ impl CodegenError {
#[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::Error),
/// 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),
}
@@ -2,17 +2,22 @@
// 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.
use crate::error::FetchMetadataError;
use codec::{Decode, Encode};
use jsonrpsee::{
async_client::ClientBuilder,
client_transport::ws::{Url, WsTransportClientBuilder},
client_transport::ws::WsTransportClientBuilder,
core::{client::ClientT, Error},
http_client::HttpClientBuilder,
rpc_params,
};
use std::time::Duration;
// Part of the public interface:
pub use jsonrpsee::client_transport::ws::Url;
/// The metadata version that is fetched from the node.
#[derive(Default, Debug, Clone, Copy)]
pub enum MetadataVersion {
@@ -44,20 +49,24 @@ impl std::str::FromStr for MetadataVersion {
}
}
/// Fetch metadata from a file.
pub fn fetch_metadata_from_file_blocking(
path: &std::path::Path,
) -> Result<Vec<u8>, 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)?;
Ok(bytes)
}
/// Returns the metadata bytes from the provided URL, blocking the current thread.
pub fn fetch_metadata_bytes_blocking(
pub fn fetch_metadata_from_url_blocking(
url: Url,
version: MetadataVersion,
) -> Result<Vec<u8>, FetchMetadataError> {
tokio_block_on(fetch_metadata_bytes(url, version))
}
/// Returns the raw, 0x prefixed metadata hex from the provided URL, blocking the current thread.
pub fn fetch_metadata_hex_blocking(
url: Url,
version: MetadataVersion,
) -> Result<String, FetchMetadataError> {
tokio_block_on(fetch_metadata_hex(url, version))
tokio_block_on(fetch_metadata_from_url(url, version))
}
// Block on some tokio runtime for sync contexts
@@ -70,7 +79,7 @@ fn tokio_block_on<T, Fut: std::future::Future<Output = T>>(fut: Fut) -> T {
}
/// Returns the metadata bytes from the provided URL.
pub async fn fetch_metadata_bytes(
pub async fn fetch_metadata_from_url(
url: Url,
version: MetadataVersion,
) -> Result<Vec<u8>, FetchMetadataError> {
@@ -83,16 +92,6 @@ pub async fn fetch_metadata_bytes(
Ok(bytes)
}
/// Returns the raw, 0x prefixed metadata hex from the provided URL.
pub async fn fetch_metadata_hex(
url: Url,
version: MetadataVersion,
) -> Result<String, FetchMetadataError> {
let bytes = fetch_metadata_bytes(url, version).await?;
let hex_data = format!("0x{}", hex::encode(bytes));
Ok(hex_data)
}
async fn fetch_metadata_ws(
url: Url,
version: MetadataVersion,
+252 -50
View File
@@ -2,65 +2,267 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! Library to generate an API for a Substrate runtime from its metadata.
//!
//! ## Generated Structure
//!
//! The API generator logic:
//! - At the root there is the `item_mod` provided (ie `pub mod api {}`)
//! - Pallets are represented by a child module (ie `pub mod PalletName {}`) of the root
//! - Each pallet exposes as child modules (if applicable):
//! - Calls (`pub mod calls {}`)
//! - Events (`pub mod events {}`)
//! - Storage (`pub mod storage {}`)
//! - Constants (`pub mod constants {}`)
//!
//! ## Example
//!
//! ```no_run
//! use std::fs;
//! use codec::Decode;
//! use subxt_metadata::Metadata;
//! use subxt_codegen::{CratePath, DerivesRegistry, TypeSubstitutes};
//!
//! let encoded = fs::read("../artifacts/polkadot_metadata_full.scale").unwrap();
//!
//! // Runtime metadata obtained from a node.
//! let metadata = Metadata::decode(&mut &*encoded).unwrap();
//! // Module under which the API is generated.
//! let item_mod = syn::parse_quote!(
//! pub mod api {}
//! );
//! // Default module derivatives.
//! let mut derives = DerivesRegistry::with_default_derives(&CratePath::default());
//! // Default type substitutes.
//! let substs = TypeSubstitutes::with_default_substitutes(&CratePath::default());
//! // Generate the Runtime API.
//! let generator = subxt_codegen::RuntimeGenerator::new(metadata);
//! // Include metadata documentation in the Runtime API.
//! let generate_docs = true;
//! let runtime_api = generator.generate_runtime(item_mod, derives, substs, CratePath::default(), generate_docs).unwrap();
//! println!("{}", runtime_api);
//! ```
//! Generate a type safe Subxt interface for a Substrate runtime from its metadata.
//! 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)]
mod api;
mod error;
mod ir;
mod types;
pub mod error;
// 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")]
pub mod utils;
pub mod fetch_metadata;
#[cfg(feature = "web")]
use getrandom as _;
pub use self::{
api::{GenerateRuntimeApi, RuntimeGenerator},
error::{CodegenError, TypeSubstitutionError},
types::{
CratePath, Derives, DerivesRegistry, Module, TypeDefGen, TypeDefParameters, TypeGenerator,
TypeSubstitutes,
},
};
use api::RuntimeGenerator;
use proc_macro2::TokenStream as TokenStream2;
use std::collections::HashMap;
// We expose these only because they are currently needed in subxt-explorer.
// Eventually we'll move the type generation stuff out into a separate crate.
#[doc(hidden)]
pub mod __private {
pub use crate::types::{DerivesRegistry, TypeDefGen, TypeGenerator, TypeSubstitutes};
}
// Part of the public interface, so expose:
pub use error::CodegenError;
pub use subxt_metadata::Metadata;
pub use syn;
/// Generate a type safe interface to use with `subxt`.
/// The options exposed here are similar to those exposed via
/// the `#[subxt]` macro or via the `subxt codegen` CLI command.
/// Both use this under the hood.
///
/// # Example
///
/// Generating an interface using all of the defaults:
///
/// ```rust
/// use codec::Decode;
/// use subxt_codegen::{ Metadata, CodegenBuilder };
///
/// // Get hold of and decode some metadata:
/// let encoded = std::fs::read("../artifacts/polkadot_metadata_full.scale").unwrap();
/// let metadata = Metadata::decode(&mut &*encoded).unwrap();
///
/// // Generate a TokenStream representing the code for the interface.
/// // This can be converted to a string, displayed as-is or output from a macro.
/// let token_stream = CodegenBuilder::new().generate(metadata);
/// ````
pub struct CodegenBuilder {
crate_path: syn::Path,
use_default_derives: bool,
use_default_substitutions: bool,
generate_docs: bool,
runtime_types_only: bool,
item_mod: syn::ItemMod,
extra_global_derives: Vec<syn::Path>,
extra_global_attributes: Vec<syn::Attribute>,
type_substitutes: HashMap<syn::Path, syn::Path>,
derives_for_type: HashMap<syn::TypePath, Vec<syn::Path>>,
attributes_for_type: HashMap<syn::TypePath, Vec<syn::Attribute>>,
}
impl Default for CodegenBuilder {
fn default() -> Self {
CodegenBuilder {
crate_path: syn::parse_quote!(::subxt),
use_default_derives: true,
use_default_substitutions: true,
generate_docs: true,
runtime_types_only: false,
item_mod: syn::parse_quote!(
pub mod api {}
),
extra_global_derives: Vec::new(),
extra_global_attributes: Vec::new(),
type_substitutes: HashMap::new(),
derives_for_type: HashMap::new(),
attributes_for_type: HashMap::new(),
}
}
}
impl CodegenBuilder {
/// Construct a builder to configure and generate a type-safe interface for Subxt.
pub fn new() -> Self {
CodegenBuilder::default()
}
/// Disable the default derives that are applied to all types.
///
/// # Warning
///
/// This is not recommended, and is highly likely to break some part of the
/// generated interface. Expect compile errors.
pub fn disable_default_derives(&mut self) {
self.use_default_derives = false;
}
/// Disable the default type substitutions that are applied to the generated
/// code.
///
/// # Warning
///
/// This is not recommended, and is highly likely to break some part of the
/// generated interface. Expect compile errors.
pub fn disable_default_substitutes(&mut self) {
self.use_default_substitutions = false;
}
/// Disable the output of doc comments associated with the generated types and
/// methods. This can reduce the generated code size at the expense of losing
/// documentation.
pub fn no_docs(&mut self) {
self.generate_docs = false;
}
/// Only generate the types, and don't generate the rest of the Subxt specific
/// interface.
pub fn runtime_types_only(&mut self) {
self.runtime_types_only = true;
}
/// Set the additional derives that will be applied to all types. By default,
/// a set of derives required for Subxt are automatically added for all types.
///
/// # Warning
///
/// Invalid derives, or derives that cannot be applied to _all_ of the generated
/// types (taking into account that some types are substituted for hand written ones
/// that we cannot add extra derives for) will lead to compile errors in the
/// generated code.
pub fn set_additional_global_derives(&mut self, derives: Vec<syn::Path>) {
self.extra_global_derives = derives;
}
/// Set the additional attributes that will be applied to all types. By default,
/// a set of attributes required for Subxt are automatically added for all types.
///
/// # Warning
///
/// Invalid attributes can very easily lead to compile errors in the generated code.
pub fn set_additional_global_attributes(&mut self, attributes: Vec<syn::Attribute>) {
self.extra_global_attributes = attributes;
}
/// Set additional derives for a specific type at the path given.
///
/// # Warning
///
/// For composite types, you may also need to set the same additional derives on all of
/// the contained types as well to avoid compile errors in the generated code.
pub fn add_derives_for_type(
&mut self,
ty: syn::TypePath,
derives: impl IntoIterator<Item = syn::Path>,
) {
self.derives_for_type.entry(ty).or_default().extend(derives);
}
/// Set additional attributes for a specific type at the path given.
///
/// # Warning
///
/// For composite types, you may also need to consider contained types and whether they need
/// similar attributes setting.
pub fn add_attributes_for_type(
&mut self,
ty: syn::TypePath,
attributes: impl IntoIterator<Item = syn::Attribute>,
) {
self.attributes_for_type
.entry(ty)
.or_default()
.extend(attributes);
}
/// Substitute a type at the given path with some type at the second path. During codegen,
/// we will avoid generating the type at the first path given, and instead point any references
/// to that type to the second path given.
///
/// The substituted type will need to implement the relevant traits to be compatible with the
/// original, and it will need to SCALE encode and SCALE decode in a compatible way.
pub fn set_type_substitute(&mut self, ty: syn::Path, with: syn::Path) {
self.type_substitutes.insert(ty, with);
}
/// By default, all of the code is generated inside a module `pub mod api {}`. We decorate
/// this module with a few attributes to reduce compile warnings and things. You can provide a
/// target module here, allowing you to add additional attributes or inner code items (with the
/// warning that duplicate identifiers will lead to compile errors).
pub fn set_target_module(&mut self, item_mod: syn::ItemMod) {
self.item_mod = item_mod;
}
/// Set the path to the `subxt` crate. By default, we expect it to be at `::subxt`.
pub fn set_subxt_crate_path(&mut self, crate_path: syn::Path) {
self.crate_path = crate_path;
}
/// Generate an interface, assuming that the default path to the `subxt` crate is `::subxt`.
/// If the `subxt` crate is not available as a top level dependency, use `generate` and provide
/// a valid path to the `subxt¦ crate.
pub fn generate(self, metadata: Metadata) -> Result<TokenStream2, CodegenError> {
let crate_path = self.crate_path;
let mut derives_registry = if self.use_default_derives {
types::DerivesRegistry::with_default_derives(&crate_path)
} else {
types::DerivesRegistry::new()
};
derives_registry.extend_for_all(self.extra_global_derives, self.extra_global_attributes);
for (ty, derives) in self.derives_for_type {
derives_registry.extend_for_type(ty, derives, vec![]);
}
for (ty, attributes) in self.attributes_for_type {
derives_registry.extend_for_type(ty, vec![], attributes);
}
let mut type_substitutes = if self.use_default_substitutions {
types::TypeSubstitutes::with_default_substitutes(&crate_path)
} else {
types::TypeSubstitutes::new()
};
for (from, with) in self.type_substitutes {
let abs_path = with.try_into()?;
type_substitutes.insert(from, abs_path)?;
}
let item_mod = self.item_mod;
let generator = RuntimeGenerator::new(metadata);
let should_gen_docs = self.generate_docs;
if self.runtime_types_only {
generator.generate_runtime_types(
item_mod,
derives_registry,
type_substitutes,
crate_path,
should_gen_docs,
)
} else {
generator.generate_runtime(
item_mod,
derives_registry,
type_substitutes,
crate_path,
should_gen_docs,
)
}
}
}
+2 -2
View File
@@ -4,7 +4,7 @@
use crate::error::CodegenError;
use super::{CratePath, Derives, Field, TypeDefParameters, TypeGenerator, TypeParameter, TypePath};
use super::{Derives, Field, TypeDefParameters, TypeGenerator, TypeParameter, TypePath};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use scale_info::{form::PortableForm, Type, TypeDef, TypeDefPrimitive};
@@ -36,7 +36,7 @@ impl CompositeDef {
field_visibility: Option<syn::Visibility>,
type_gen: &TypeGenerator,
docs: &[String],
crate_path: &CratePath,
crate_path: &syn::Path,
) -> Result<Self, CodegenError> {
let mut derives = type_gen.type_derives(ty)?;
let fields: Vec<_> = fields_def.field_types().collect();
+3 -4
View File
@@ -2,7 +2,6 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use crate::CratePath;
use syn::{parse_quote, Path};
use std::collections::{HashMap, HashSet};
@@ -35,7 +34,7 @@ impl DerivesRegistry {
///
/// The `crate_path` denotes the `subxt` crate access path in the
/// generated code.
pub fn with_default_derives(crate_path: &CratePath) -> Self {
pub fn with_default_derives(crate_path: &syn::Path) -> Self {
Self {
default_derives: Derives::with_defaults(crate_path),
specific_type_derives: Default::default(),
@@ -117,7 +116,7 @@ impl Derives {
/// Creates a new instance of `Derives` with the `crate_path` prepended
/// to the set of default derives that reside in `subxt`.
pub fn with_defaults(crate_path: &CratePath) -> Self {
pub fn with_defaults(crate_path: &syn::Path) -> Self {
let mut derives = HashSet::new();
let mut attributes = HashSet::new();
@@ -148,7 +147,7 @@ impl Derives {
}
/// Add `#crate_path::ext::codec::CompactAs` to the derives.
pub fn insert_codec_compact_as(&mut self, crate_path: &CratePath) {
pub fn insert_codec_compact_as(&mut self, crate_path: &syn::Path) {
self.insert_derive(parse_quote!(#crate_path::ext::codec::CompactAs));
}
+2 -54
View File
@@ -41,7 +41,7 @@ pub struct TypeGenerator<'a> {
/// Set of derives with which to annotate generated types.
derives: DerivesRegistry,
/// The `subxt` crate access path in the generated code.
crate_path: CratePath,
crate_path: syn::Path,
/// True if codegen should generate the documentation for the API.
should_gen_docs: bool,
}
@@ -53,7 +53,7 @@ impl<'a> TypeGenerator<'a> {
root_mod: &'static str,
type_substitutes: TypeSubstitutes,
derives: DerivesRegistry,
crate_path: CratePath,
crate_path: syn::Path,
should_gen_docs: bool,
) -> Self {
let root_mod_ident = Ident::new(root_mod, Span::call_site());
@@ -334,55 +334,3 @@ impl Module {
&self.root_mod
}
}
/// A newtype wrapper which stores the path to the Subxt crate.
#[derive(Debug, Clone)]
pub struct CratePath(syn::Path);
impl CratePath {
/// Create a new `CratePath` from a `syn::Path`.
pub fn new(path: syn::Path) -> Self {
Self(path)
}
}
impl Default for CratePath {
fn default() -> Self {
Self(syn::parse_quote!(::subxt))
}
}
impl From<syn::Path> for CratePath {
fn from(path: syn::Path) -> Self {
CratePath::new(path)
}
}
impl ToTokens for CratePath {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.0.to_tokens(tokens)
}
}
impl From<&str> for CratePath {
fn from(crate_path: &str) -> Self {
Self(syn::parse_str(crate_path).unwrap_or_else(|err| {
panic!("failed converting {crate_path:?} to `syn::Path`: {err:?}");
}))
}
}
impl From<String> for CratePath {
fn from(crate_path: String) -> Self {
CratePath::from(crate_path.as_str())
}
}
impl From<Option<String>> for CratePath {
fn from(maybe_crate_path: Option<String>) -> Self {
match maybe_crate_path {
None => CratePath::default(),
Some(crate_path) => crate_path.into(),
}
}
}
+2 -2
View File
@@ -2,7 +2,7 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use crate::{error::TypeSubstitutionError, CratePath};
use crate::error::TypeSubstitutionError;
use std::{borrow::Borrow, collections::HashMap};
use syn::{parse_quote, spanned::Spanned as _};
@@ -55,7 +55,7 @@ impl TypeSubstitutes {
///
/// The `crate_path` denotes the `subxt` crate access path in the
/// generated code.
pub fn with_default_substitutes(crate_path: &CratePath) -> Self {
pub fn with_default_substitutes(crate_path: &syn::Path) -> Self {
// Some hardcoded default type substitutes, can be overridden by user
let defaults = [
(
+22 -22
View File
@@ -34,7 +34,7 @@ fn generate_struct_with_primitives() {
registry.register_type(&meta_type::<S>());
let portable_types: PortableRegistry = registry.into();
let crate_path = "::subxt_path".into();
let crate_path = syn::parse_str("::subxt_path").unwrap();
let type_gen = TypeGenerator::new(
&portable_types,
"root",
@@ -86,7 +86,7 @@ fn generate_struct_with_a_struct_field() {
registry.register_type(&meta_type::<Parent>());
let portable_types: PortableRegistry = registry.into();
let crate_path = "::subxt_path".into();
let crate_path = syn::parse_str("::subxt_path").unwrap();
let type_gen = TypeGenerator::new(
&portable_types,
"root",
@@ -140,7 +140,7 @@ fn generate_tuple_struct() {
registry.register_type(&meta_type::<Parent>());
let portable_types: PortableRegistry = registry.into();
let crate_path = "::subxt_path".into();
let crate_path = syn::parse_str("::subxt_path").unwrap();
let type_gen = TypeGenerator::new(
&portable_types,
"root",
@@ -231,7 +231,7 @@ fn derive_compact_as_for_uint_wrapper_structs() {
registry.register_type(&meta_type::<TSu128>());
let portable_types: PortableRegistry = registry.into();
let crate_path = "::subxt_path".into();
let crate_path = syn::parse_str("::subxt_path").unwrap();
let type_gen = TypeGenerator::new(
&portable_types,
"root",
@@ -328,7 +328,7 @@ fn generate_enum() {
registry.register_type(&meta_type::<E>());
let portable_types: PortableRegistry = registry.into();
let crate_path = "::subxt_path".into();
let crate_path = syn::parse_str("::subxt_path").unwrap();
let type_gen = TypeGenerator::new(
&portable_types,
"root",
@@ -392,7 +392,7 @@ fn compact_fields() {
registry.register_type(&meta_type::<E>());
let portable_types: PortableRegistry = registry.into();
let crate_path = "::subxt_path".into();
let crate_path = syn::parse_str("::subxt_path").unwrap();
let type_gen = TypeGenerator::new(
&portable_types,
"root",
@@ -460,7 +460,7 @@ fn compact_generic_parameter() {
registry.register_type(&meta_type::<S>());
let portable_types: PortableRegistry = registry.into();
let crate_path = "::subxt_path".into();
let crate_path = syn::parse_str("::subxt_path").unwrap();
let type_gen = TypeGenerator::new(
&portable_types,
"root",
@@ -507,7 +507,7 @@ fn generate_array_field() {
registry.register_type(&meta_type::<S>());
let portable_types: PortableRegistry = registry.into();
let crate_path = "::subxt_path".into();
let crate_path = syn::parse_str("::subxt_path").unwrap();
let type_gen = TypeGenerator::new(
&portable_types,
"root",
@@ -550,7 +550,7 @@ fn option_fields() {
registry.register_type(&meta_type::<S>());
let portable_types: PortableRegistry = registry.into();
let crate_path = "::subxt_path".into();
let crate_path = syn::parse_str("::subxt_path").unwrap();
let type_gen = TypeGenerator::new(
&portable_types,
"root",
@@ -596,7 +596,7 @@ fn box_fields_struct() {
registry.register_type(&meta_type::<S>());
let portable_types: PortableRegistry = registry.into();
let crate_path = "::subxt_path".into();
let crate_path = syn::parse_str("::subxt_path").unwrap();
let type_gen = TypeGenerator::new(
&portable_types,
"root",
@@ -642,7 +642,7 @@ fn box_fields_enum() {
registry.register_type(&meta_type::<E>());
let portable_types: PortableRegistry = registry.into();
let crate_path = "::subxt_path".into();
let crate_path = syn::parse_str("::subxt_path").unwrap();
let type_gen = TypeGenerator::new(
&portable_types,
"root",
@@ -688,7 +688,7 @@ fn range_fields() {
registry.register_type(&meta_type::<S>());
let portable_types: PortableRegistry = registry.into();
let crate_path = "::subxt_path".into();
let crate_path = syn::parse_str("::subxt_path").unwrap();
let type_gen = TypeGenerator::new(
&portable_types,
"root",
@@ -738,7 +738,7 @@ fn generics() {
registry.register_type(&meta_type::<Bar>());
let portable_types: PortableRegistry = registry.into();
let crate_path = "::subxt_path".into();
let crate_path = syn::parse_str("::subxt_path").unwrap();
let type_gen = TypeGenerator::new(
&portable_types,
"root",
@@ -795,7 +795,7 @@ fn generics_nested() {
registry.register_type(&meta_type::<Bar<bool>>());
let portable_types: PortableRegistry = registry.into();
let crate_path = "::subxt_path".into();
let crate_path = syn::parse_str("::subxt_path").unwrap();
let type_gen = TypeGenerator::new(
&portable_types,
"root",
@@ -852,7 +852,7 @@ fn generate_bitvec() {
registry.register_type(&meta_type::<S>());
let portable_types: PortableRegistry = registry.into();
let crate_path = "::subxt_path".into();
let crate_path = syn::parse_str("::subxt_path").unwrap();
let type_gen = TypeGenerator::new(
&portable_types,
"root",
@@ -911,7 +911,7 @@ fn generics_with_alias_adds_phantom_data_marker() {
registry.register_type(&meta_type::<UnnamedFields<bool, bool>>());
let portable_types: PortableRegistry = registry.into();
let crate_path = "::subxt_path".into();
let crate_path = syn::parse_str("::subxt_path").unwrap();
let type_gen = TypeGenerator::new(
&portable_types,
"root",
@@ -982,7 +982,7 @@ fn modules() {
registry.register_type(&meta_type::<m::c::Foo>());
let portable_types: PortableRegistry = registry.into();
let crate_path = "::subxt_path".into();
let crate_path = syn::parse_str("::subxt_path").unwrap();
let type_gen = TypeGenerator::new(
&portable_types,
"root",
@@ -1051,7 +1051,7 @@ fn dont_force_struct_names_camel_case() {
registry.register_type(&meta_type::<AB>());
let portable_types: PortableRegistry = registry.into();
let crate_path = "::subxt_path".into();
let crate_path = syn::parse_str("::subxt_path").unwrap();
let type_gen = TypeGenerator::new(
&portable_types,
"root",
@@ -1094,7 +1094,7 @@ fn apply_user_defined_derives_for_all_types() {
registry.register_type(&meta_type::<A>());
let portable_types: PortableRegistry = registry.into();
let crate_path = "::subxt_path".into();
let crate_path = syn::parse_str("::subxt_path").unwrap();
// configure derives
let mut derives = DerivesRegistry::with_default_derives(&crate_path);
derives.extend_for_all(
@@ -1156,7 +1156,7 @@ fn apply_user_defined_derives_for_specific_types() {
registry.register_type(&meta_type::<A>());
let portable_types: PortableRegistry = registry.into();
let crate_path = "::subxt_path".into();
let crate_path = syn::parse_str("::subxt_path").unwrap();
// configure derives
let mut derives = DerivesRegistry::with_default_derives(&crate_path);
// for all types
@@ -1233,7 +1233,7 @@ fn opt_out_from_default_derives() {
registry.register_type(&meta_type::<A>());
let portable_types: PortableRegistry = registry.into();
let crate_path = "::subxt_path".into();
let crate_path = syn::parse_str("::subxt_path").unwrap();
// configure derives
let mut derives = DerivesRegistry::new();
derives.extend_for_all(
@@ -1293,7 +1293,7 @@ fn opt_out_from_default_substitutes() {
registry.register_type(&meta_type::<S>());
let portable_types: PortableRegistry = registry.into();
let crate_path = "::subxt_path".into();
let crate_path = syn::parse_str("::subxt_path").unwrap();
let type_gen = TypeGenerator::new(
&portable_types,
"root",
+2 -3
View File
@@ -5,8 +5,7 @@
use crate::error::CodegenError;
use super::{
CompositeDef, CompositeDefFields, CratePath, Derives, TypeDefParameters, TypeGenerator,
TypeParameter,
CompositeDef, CompositeDefFields, Derives, TypeDefParameters, TypeGenerator, TypeParameter,
};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
@@ -34,7 +33,7 @@ impl TypeDefGen {
pub fn from_type(
ty: &Type<PortableForm>,
type_gen: &TypeGenerator,
crate_path: &CratePath,
crate_path: &syn::Path,
should_gen_docs: bool,
) -> Result<Self, CodegenError> {
let derives = type_gen.type_derives(ty)?;
+2 -4
View File
@@ -2,8 +2,6 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use crate::CratePath;
use proc_macro2::{Ident, TokenStream};
use quote::format_ident;
use scale_info::{form::PortableForm, Path, TypeDefPrimitive};
@@ -121,12 +119,12 @@ pub enum TypePathType {
Compact {
inner: Box<TypePath>,
is_field: bool,
crate_path: CratePath,
crate_path: syn::Path,
},
BitVec {
bit_order_type: Box<TypePath>,
bit_store_type: Box<TypePath>,
crate_path: CratePath,
crate_path: syn::Path,
},
}
-15
View File
@@ -1,15 +0,0 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! Utilities to help with fetching and decoding metadata.
mod fetch_metadata;
// easy access to this type needed for fetching metadata:
pub use jsonrpsee::client_transport::ws::Url;
pub use fetch_metadata::{
fetch_metadata_bytes, fetch_metadata_bytes_blocking, fetch_metadata_hex,
fetch_metadata_hex_blocking, MetadataVersion,
};
+2 -1
View File
@@ -20,7 +20,8 @@ web = ["subxt-codegen/web"]
proc-macro = true
[dependencies]
codec = { package = "parity-scale-codec", workspace = true }
darling = { workspace = true }
proc-macro-error = { workspace = true }
syn = { workspace = true }
subxt-codegen = { workspace = true }
subxt-codegen = { workspace = true, features = ["fetch-metadata"] }
+71 -57
View File
@@ -4,11 +4,15 @@
extern crate proc_macro;
use codec::Decode;
use darling::{ast::NestedMeta, FromMeta};
use proc_macro::TokenStream;
use proc_macro_error::{abort_call_site, proc_macro_error};
use subxt_codegen::{
utils::Url, CodegenError, DerivesRegistry, GenerateRuntimeApi, TypeSubstitutes,
fetch_metadata::{
fetch_metadata_from_file_blocking, fetch_metadata_from_url_blocking, MetadataVersion, Url,
},
CodegenBuilder, CodegenError,
};
use syn::{parse_macro_input, punctuated::Punctuated};
@@ -38,7 +42,7 @@ struct RuntimeMetadataArgs {
#[darling(multiple)]
substitute_type: Vec<SubstituteType>,
#[darling(default, rename = "crate")]
crate_path: Option<String>,
crate_path: Option<syn::Path>,
#[darling(default)]
generate_docs: darling::util::Flag,
#[darling(default)]
@@ -85,62 +89,61 @@ pub fn subxt(args: TokenStream, input: TokenStream) -> TokenStream {
Err(e) => return TokenStream::from(e.write_errors()),
};
let crate_path = match args.crate_path {
Some(crate_path) => crate_path.into(),
None => subxt_codegen::CratePath::default(),
};
let mut derives_registry = if args.no_default_derives {
DerivesRegistry::new()
} else {
DerivesRegistry::with_default_derives(&crate_path)
};
let mut codegen = CodegenBuilder::new();
let universal_derives = args.derive_for_all_types.unwrap_or_default();
let universal_attributes = args.attributes_for_all_types.unwrap_or_default();
derives_registry.extend_for_all(
universal_derives,
universal_attributes.iter().map(|a| a.0.clone()),
// Use the item module that the macro is on:
codegen.set_target_module(item_mod);
// Use the provided crate path:
if let Some(crate_path) = args.crate_path {
codegen.set_subxt_crate_path(crate_path)
}
// Respect the boolean flags:
if args.runtime_types_only {
codegen.runtime_types_only();
}
if args.no_default_derives {
codegen.disable_default_derives();
}
if args.no_default_substitutions {
codegen.disable_default_substitutes();
}
if !args.generate_docs.is_present() {
codegen.no_docs()
}
// Configure derives:
codegen.set_additional_global_derives(
args.derive_for_all_types
.unwrap_or_default()
.into_iter()
.collect(),
);
for derives in &args.derive_for_type {
derives_registry.extend_for_type(
derives.path.clone(),
derives.derive.iter().cloned(),
vec![],
)
}
for attributes in &args.attributes_for_type {
derives_registry.extend_for_type(
attributes.path.clone(),
vec![],
attributes.attributes.iter().map(|a| a.0.clone()),
)
for d in args.derive_for_type {
codegen.add_derives_for_type(d.path, d.derive.into_iter());
}
let mut type_substitutes = if args.no_default_substitutions {
TypeSubstitutes::new()
} else {
TypeSubstitutes::with_default_substitutes(&crate_path)
};
let substitute_args_res: Result<(), _> = args.substitute_type.into_iter().try_for_each(|sub| {
sub.with
.try_into()
.and_then(|with| type_substitutes.insert(sub.path, with))
});
if let Err(err) = substitute_args_res {
return CodegenError::from(err).into_compile_error().into();
// Configure attributes:
codegen.set_additional_global_attributes(
args.attributes_for_all_types
.unwrap_or_default()
.into_iter()
.map(|a| a.0)
.collect(),
);
for d in args.attributes_for_type {
codegen.add_attributes_for_type(d.path, d.attributes.into_iter().map(|a| a.0))
}
let should_gen_docs = args.generate_docs.is_present();
// Insert type substitutions:
for sub in args.substitute_type.into_iter() {
codegen.set_type_substitute(sub.path, sub.with);
}
// Do we want to fetch unstable metadata? This only works if fetching from a URL.
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 {
@@ -152,20 +155,31 @@ pub fn subxt(args: TokenStream, input: TokenStream) -> TokenStream {
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);
let generated_code = fetch_metadata_from_file_blocking(&path)
.map_err(CodegenError::from)
.and_then(|b| subxt_codegen::Metadata::decode(&mut &*b).map_err(Into::into))
.and_then(|m| codegen.generate(m).map_err(Into::into))
.unwrap_or_else(|e| e.into_compile_error());
runtime_api_generator
.generate_from_path(path)
.map_or_else(|err| err.into_compile_error().into(), Into::into)
generated_code.into()
}
(None, Some(url_string)) => {
let url = Url::parse(&url_string).unwrap_or_else(|_| {
abort_call_site!("Cannot download metadata; invalid url: {}", url_string)
});
runtime_api_generator
.unstable_metadata(unstable_metadata)
.generate_from_url(url)
.map_or_else(|err| err.into_compile_error().into(), Into::into)
let version = match unstable_metadata {
true => MetadataVersion::Unstable,
false => MetadataVersion::Latest,
};
let generated_code = fetch_metadata_from_url_blocking(url, version)
.map_err(CodegenError::from)
.and_then(|b| subxt_codegen::Metadata::decode(&mut &*b).map_err(Into::into))
.and_then(|m| codegen.generate(m).map_err(Into::into))
.unwrap_or_else(|e| e.into_compile_error());
generated_code.into()
}
(None, None) => {
abort_call_site!(
+1 -1
View File
@@ -309,7 +309,7 @@ pub mod ext {
/// 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
/// ```rust,ignore
/// #[subxt::subxt(
/// runtime_metadata_url = "wss://rpc.polkadot.io:443",
/// unstable_metadata
@@ -7,7 +7,7 @@ use frame_metadata::{
RuntimeMetadataPrefixed,
};
use scale_info::{meta_type, IntoPortable, PortableRegistry, Registry, TypeInfo};
use subxt_codegen::{CratePath, DerivesRegistry, RuntimeGenerator, TypeSubstitutes};
use subxt_codegen::CodegenBuilder;
use syn::__private::quote;
fn generate_runtime_interface_from_metadata(metadata: RuntimeMetadataPrefixed) -> String {
@@ -15,15 +15,10 @@ fn generate_runtime_interface_from_metadata(metadata: RuntimeMetadataPrefixed) -
let metadata = metadata
.try_into()
.expect("frame_metadata should be convertible into Metadata");
let generator = RuntimeGenerator::new(metadata);
let item_mod = syn::parse_quote!(
pub mod api {}
);
let crate_path = CratePath::default();
let derives = DerivesRegistry::with_default_derives(&crate_path);
let type_substitutes = TypeSubstitutes::with_default_substitutes(&crate_path);
generator
.generate_runtime(item_mod, derives, type_substitutes, crate_path, false)
CodegenBuilder::new()
.no_docs()
.generate(metadata)
.expect("API generation must be valid")
.to_string()
}
@@ -4,7 +4,7 @@
use codec::Decode;
use regex::Regex;
use subxt_codegen::{CratePath, DerivesRegistry, RuntimeGenerator, TypeSubstitutes};
use subxt_codegen::{ syn, CodegenBuilder };
use subxt_metadata::Metadata;
fn load_test_metadata() -> Metadata {
@@ -48,25 +48,18 @@ fn metadata_docs() -> Vec<String> {
docs
}
fn generate_runtime_interface(crate_path: CratePath, should_gen_docs: bool) -> String {
fn generate_runtime_interface(should_gen_docs: bool) -> String {
// Load the runtime metadata downloaded from a node via `test-runtime`.
let metadata = load_test_metadata();
// Generate a runtime interface from the provided metadata.
let generator = RuntimeGenerator::new(metadata);
let item_mod = syn::parse_quote!(
pub mod api {}
);
let derives = DerivesRegistry::with_default_derives(&crate_path);
let type_substitutes = TypeSubstitutes::with_default_substitutes(&crate_path);
generator
.generate_runtime(
item_mod,
derives,
type_substitutes,
crate_path,
should_gen_docs,
)
let mut codegen = CodegenBuilder::new();
if !should_gen_docs {
codegen.no_docs();
}
codegen
.generate(metadata)
.expect("API generation must be valid")
.to_string()
}
@@ -74,7 +67,7 @@ fn generate_runtime_interface(crate_path: CratePath, should_gen_docs: bool) -> S
fn interface_docs(should_gen_docs: bool) -> Vec<String> {
// Generate the runtime interface from the node's metadata.
// Note: the API is generated on a single line.
let runtime_api = generate_runtime_interface(CratePath::default(), should_gen_docs);
let runtime_api = generate_runtime_interface(should_gen_docs);
// Documentation lines have the following format:
// # [ doc = "Upward message is invalid XCM."]
@@ -145,18 +138,10 @@ fn check_root_attrs_preserved() {
pub mod api {}
);
// Generate a runtime interface from the provided metadata.
let generator = RuntimeGenerator::new(metadata);
let derives = DerivesRegistry::with_default_derives(&CratePath::default());
let type_substitutes = TypeSubstitutes::with_default_substitutes(&CratePath::default());
let generated_code = generator
.generate_runtime(
item_mod,
derives,
type_substitutes,
CratePath::default(),
true,
)
let mut codegen = CodegenBuilder::new();
codegen.set_target_module(item_mod);
let generated_code = codegen
.generate(metadata)
.expect("API generation must be valid")
.to_string();