Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

codegen: Fetch and decode metadata version then fallback #1092

Merged
merged 8 commits into from
Jul 25, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 42 additions & 6 deletions codegen/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ where
/// * `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.
/// * `unstable_metadata` - Whether to fetch the unstable metadata first.
///
/// **Note:** This is a wrapper over [RuntimeGenerator] for static metadata use-cases.
pub fn generate_runtime_api_from_url(
Expand All @@ -93,16 +94,29 @@ pub fn generate_runtime_api_from_url(
crate_path: CratePath,
should_gen_docs: bool,
runtime_types_only: bool,
unstable_metadata: 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)?,
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 = 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_from_bytes(
generate_runtime_api_with_metadata(
item_mod,
&bytes,
metadata,
derives,
type_substitutes,
crate_path,
Expand Down Expand Up @@ -135,6 +149,28 @@ pub fn generate_runtime_api_from_bytes(
) -> Result<TokenStream2, CodegenError> {
let metadata = Metadata::decode(&mut &bytes[..])?;

generate_runtime_api_with_metadata(
item_mod,
metadata,
derives,
type_substitutes,
crate_path,
should_gen_docs,
runtime_types_only,
)
}

/// Similar to [`generate_runtime_api_from_bytes`] that works with decoded `subxt::Metadata` instead
/// of raw bytes.
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(
Expand Down
10 changes: 10 additions & 0 deletions macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ struct RuntimeMetadataArgs {
no_default_derives: bool,
#[darling(default)]
no_default_substitutions: bool,
#[darling(default)]
unstable_metadata: darling::util::Flag,
}

#[derive(Debug, FromMeta)]
Expand Down Expand Up @@ -131,8 +133,15 @@ 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();
match (args.runtime_metadata_path, args.runtime_metadata_url) {
(Some(rest_of_path), None) => {
if unstable_metadata {
abort_call_site!(
"The 'unstable_metadata' is expected only for `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);
Expand All @@ -159,6 +168,7 @@ pub fn subxt(args: TokenStream, input: TokenStream) -> TokenStream {
crate_path,
should_gen_docs,
args.runtime_types_only,
unstable_metadata,
)
.map_or_else(|err| err.into_compile_error().into(), Into::into)
}
Expand Down
16 changes: 16 additions & 0 deletions subxt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -294,6 +295,21 @@ pub mod ext {
/// mod polkadot {}
/// ```
///
/// ## `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 fetch the unstable version of the metadata.
/// This can be useful in CI, but is **not recommended** in production code, since it runs at compile time and will cause
/// compilation to fail if the node at the given address is unavailable or unresponsive.
///
/// ```rust,no_run
/// #[subxt::subxt(
/// runtime_metadata_url = "wss://rpc.polkadot.io:443"
/// unstable_metadata,
/// )]
/// mod polkadot {}
/// ```
///
/// **Note**: At the moment, you must derive at least one of `codec::Encode` or `codec::Decode` or `scale_encode::EncodeAsType` or
/// `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
Expand Down