Skip to content

Commit

Permalink
Tidy subxt-codegen crate interface (#1225)
Browse files Browse the repository at this point in the history
* first pass tidying codegen crate interface

* fix a codegen test

* macro: keep error spans

* clippy

* fix doc example

* removecommented-out code
  • Loading branch information
jsdw authored Oct 27, 2023
1 parent 98170c8 commit 2b21f8d
Show file tree
Hide file tree
Showing 29 changed files with 502 additions and 553 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
112 changes: 61 additions & 51 deletions cli/src/commands/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down Expand Up @@ -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<_>, _>>()?;

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![]);
.collect::<Result<Vec<_>, _>>()
.map_err(|e| eyre!("Cannot parse global attributes: {e}"))?;
codegen.set_additional_global_attributes(universal_attributes);

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));
}
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));
}

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(())
}
4 changes: 2 additions & 2 deletions cli/src/commands/compatibility.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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)
}
15 changes: 5 additions & 10 deletions cli/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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?)
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion codegen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]

Expand Down
7 changes: 2 additions & 5 deletions codegen/src/api/calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions codegen/src/api/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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.
Expand Down
12 changes: 6 additions & 6 deletions codegen/src/api/custom_values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@

use std::collections::HashSet;

use crate::{types::TypeGenerator, CratePath};
use crate::types::TypeGenerator;
use heck::ToSnakeCase as _;
use subxt_metadata::{CustomValueMetadata, Metadata};

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();
Expand All @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions codegen/src/api/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down
Loading

0 comments on commit 2b21f8d

Please sign in to comment.