mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-23 00:18:00 +00:00
8203679cbd
* v0.50.0: Integrate frame-decode, redo storage APIs and break up Error. (#2100) * WIP integrating new frame-decode and working out new storage APIS * WIP: first pass adding new storage things to subxt-core * Second pass over Address type and start impl in Subxt * WIP new storage APIs * WIP New storage APIs roughly completed, lots of errors still * Remove PlainorMap enum; plain and map values now use same struct to simplify usage * Begin 'fixing' errors * WIP splitting errors and tidying payload/address traits * Get subxt-core compiling * Small fixes in subxt-core and remove metadata mod * subxt-core: cargo check --all-targets passes * Fix test * WIP starting to update subxt from subxt-core changes * WIP splitting up subxt errors into smaller variants * WIP errors: add DispatchError errors * Port new Storage APIs to subxt-core * cargo check -p subxt passes * Quick-fix errors in subxt-cli (explore subcommand) * fmt * Finish fixing codegen up and start fixing examples * get Subxt examples compiling and bytes_at for constants * Add some arcs to limit lifetimes in subxt/subxt-core storage APIs * A little Arcing to allow more method chaining in Storage APIs, aligning with Subxt * Update codegen test * cargo check --all-targets passing * cargo check --features 'unstable-light-client' passing * clippy * Remove unused dep in subxt * use published frame-decode * fix wasm-example * Add new tx extension to fix daily tests * Remove unused subxt_core::dynamic::DecodedValue type * Update book to match changes * Update docs to fix more broken bits * Add missing docs * fmt * allow larger result errs for now * Add missing alloc imports in subxt-core * Fix doc tests and fix bug getting constant info * Fix V14 -> Metadata transform for storage & constants * Fix parachain example * Fix FFI example * BlockLength decodes t ostruct, not u128 * use fetch/iter shorthands rather than entry in most storage tests * Fix some integration tests * Fix Runtime codegen tests * Expose the dynamic custom_value selecter and use in a UI test * Update codegen metadata * Tidy CLI storage query and support (str,str) as a storage address * Add (str,str) as valid constant address too * Show string tuple in constants example * Via the magic of traits, avoid needing any clones of queries/addresses and accept references to them * clippy * [v0.50] update scale-info-legacy and frame-decode to latest (#2119) * bump scale-info-legacy and frame-decode to latest * Remove something we don't need in this PR * Fully remove unused for now dep * [v0.50] Convert historic metadata to subxt::Metadata (#2120) * First pass converting historic metadatas to our subxt::Metadata type * use published frame-decode * fmt and rename legacy metadata macro * Enable legacy feature where needed in subxt_metadata so it compiles on its own * Use cargo hack more in CI and fix subxt-metadata features * Add tests for metadata conversion (need to optimise; some too expensive right now * Address performance and equality issues in metadata conversion testing * fmt * fmt all * clippy * Fix a doc link * Test codegen and fixes to make it work * Remove local frame-decode patch * bump frame-decode to latest * [v0.50.0] Allow visiting extrinsic fields in subxt_historic (#2124) * Allow visiting extrinsic fields * fmt * Don't use local scale-decode dep * Clippy and tidy * Extend 'subxt codegen' CLI to work with legacy metadatas * Simplify historic extrinsics example now that AccountId32s have paths/names * clippy * clippy * clippy.. * Allow visiting storage values, too, and clean up extrinsic visiting a little by narrowing lifetime * Try to fix flaky test * Add custom value decode to extrinsics example * Remove useless else branch ra thought I needed * Simplify examples * Prep to release v0.0.5 (#2126)
471 lines
18 KiB
Rust
471 lines
18 KiB
Rust
// Copyright 2019-2025 Parity Technologies (UK) Ltd.
|
|
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
|
// see LICENSE for license details.
|
|
|
|
use crate::utils::{FileOrUrl, validate_url_security};
|
|
use clap::Parser as ClapParser;
|
|
use color_eyre::eyre::eyre;
|
|
use scale_typegen_description::scale_typegen::typegen::{
|
|
settings::substitutes::path_segments,
|
|
validation::{registry_contains_type_path, similar_type_paths_in_registry},
|
|
};
|
|
use std::path::PathBuf;
|
|
use subxt_codegen::CodegenBuilder;
|
|
use subxt_metadata::Metadata;
|
|
|
|
/// Generate runtime API client code from metadata.
|
|
///
|
|
/// # Example (with code formatting)
|
|
///
|
|
/// `subxt codegen | rustfmt --edition=2018 --emit=stdout`
|
|
#[derive(Debug, ClapParser)]
|
|
pub struct Opts {
|
|
#[command(flatten)]
|
|
file_or_url: FileOrUrl,
|
|
/// Additional derives
|
|
#[clap(long = "derive")]
|
|
derives: Vec<String>,
|
|
/// Additional attributes
|
|
#[clap(long = "attribute")]
|
|
attributes: Vec<String>,
|
|
/// Path to legacy type definitions (required for metadatas pre-V14)
|
|
#[clap(long)]
|
|
legacy_types: Option<PathBuf>,
|
|
/// The spec version of the legacy metadata (required for metadatas pre-V14)
|
|
#[clap(long)]
|
|
legacy_spec_version: Option<u64>,
|
|
/// Additional derives for a given type.
|
|
///
|
|
/// Example 1: `--derive-for-type my_module::my_type=serde::Serialize`.
|
|
/// Example 2: `--derive-for-type my_module::my_type=serde::Serialize,recursive`.
|
|
#[clap(long = "derive-for-type", value_parser = derive_for_type_parser)]
|
|
derives_for_type: Vec<DeriveForType>,
|
|
/// Additional attributes for a given type.
|
|
///
|
|
/// Example 1: `--attributes-for-type my_module::my_type=#[allow(clippy::all)]`.
|
|
/// Example 2: `--attributes-for-type my_module::my_type=#[allow(clippy::all)],recursive`.
|
|
#[clap(long = "attributes-for-type", value_parser = attributes_for_type_parser)]
|
|
attributes_for_type: Vec<AttributeForType>,
|
|
/// Substitute a type for another.
|
|
///
|
|
/// Example `--substitute-type sp_runtime::MultiAddress<A,B>=subxt::utils::Static<::sp_runtime::MultiAddress<A,B>>`
|
|
#[clap(long = "substitute-type", value_parser = substitute_type_parser)]
|
|
substitute_types: Vec<(String, String)>,
|
|
/// The `subxt` crate access path in the generated code.
|
|
/// Defaults to `::subxt::ext::subxt_core`.
|
|
#[clap(long = "crate")]
|
|
crate_path: Option<String>,
|
|
/// Do not generate documentation for the runtime API code.
|
|
///
|
|
/// Defaults to `false` (documentation is generated).
|
|
#[clap(long, action)]
|
|
no_docs: bool,
|
|
/// Whether to limit code generation to only runtime types.
|
|
///
|
|
/// Defaults to `false` (all types are generated).
|
|
#[clap(long)]
|
|
runtime_types_only: bool,
|
|
/// Do not provide default trait derivations for the generated types.
|
|
///
|
|
/// Defaults to `false` (default trait derivations are provided).
|
|
#[clap(long)]
|
|
no_default_derives: bool,
|
|
/// Do not provide default substitutions for the generated types.
|
|
///
|
|
/// Defaults to `false` (default substitutions are provided).
|
|
#[clap(long)]
|
|
no_default_substitutions: bool,
|
|
/// Allow insecure URLs e.g. URLs starting with ws:// or http:// without SSL encryption
|
|
#[clap(long, short)]
|
|
allow_insecure: bool,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
struct DeriveForType {
|
|
type_path: String,
|
|
trait_path: String,
|
|
recursive: bool,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
struct AttributeForType {
|
|
type_path: String,
|
|
attribute: String,
|
|
recursive: bool,
|
|
}
|
|
|
|
fn derive_for_type_parser(src: &str) -> Result<DeriveForType, String> {
|
|
let (type_path, trait_path, recursive) = type_map_parser(src)
|
|
.ok_or_else(|| String::from("Invalid pattern for `derive-for-type`. It should be `type=derive` or `type=derive,recursive`, like `my_type=serde::Serialize` or `my_type=serde::Serialize,recursive`"))?;
|
|
Ok(DeriveForType {
|
|
type_path: type_path.to_string(),
|
|
trait_path: trait_path.to_string(),
|
|
recursive,
|
|
})
|
|
}
|
|
|
|
fn attributes_for_type_parser(src: &str) -> Result<AttributeForType, String> {
|
|
let (type_path, attribute, recursive) = type_map_parser(src)
|
|
.ok_or_else(|| String::from("Invalid pattern for `attributes-for-type`. It should be `type=attribute` like `my_type=serde::#[allow(clippy::all)]` or `type=attribute,recursive` like `my_type=serde::#[allow(clippy::all)],recursive`"))?;
|
|
Ok(AttributeForType {
|
|
type_path: type_path.to_string(),
|
|
attribute: attribute.to_string(),
|
|
recursive,
|
|
})
|
|
}
|
|
|
|
/// Parses a `&str` of the form `str1=str2` into `(str1, str2, false)` or `str1=str2,recursive` into `(str1, str2, true)`.
|
|
///
|
|
/// A `None` value returned is a parsing error.
|
|
fn type_map_parser(src: &str) -> Option<(&str, &str, bool)> {
|
|
let (str1, rest) = src.split_once('=')?;
|
|
|
|
let mut split_rest = rest.split(',');
|
|
let str2 = split_rest
|
|
.next()
|
|
.expect("split iter always returns at least one element; qed");
|
|
|
|
let mut recursive = false;
|
|
for r in split_rest {
|
|
match r {
|
|
// Note: later we can add other attributes to this match
|
|
"recursive" => {
|
|
recursive = true;
|
|
}
|
|
_ => return None,
|
|
}
|
|
}
|
|
|
|
Some((str1, str2, recursive))
|
|
}
|
|
|
|
fn substitute_type_parser(src: &str) -> Result<(String, String), String> {
|
|
let (from, to) = src
|
|
.split_once('=')
|
|
.ok_or_else(|| String::from("Invalid pattern for `substitute-type`. It should be something like `input::Type<A>=replacement::Type<A>`"))?;
|
|
|
|
Ok((from.to_string(), to.to_string()))
|
|
}
|
|
|
|
pub async fn run(opts: Opts, output: &mut impl std::io::Write) -> color_eyre::Result<()> {
|
|
validate_url_security(opts.file_or_url.url.as_ref(), opts.allow_insecure)?;
|
|
|
|
let bytes = opts.file_or_url.fetch().await?;
|
|
let legacy_types = opts
|
|
.legacy_types
|
|
.map(|path| {
|
|
let bytes = std::fs::read(path).map_err(|e| eyre!("Cannot read legacy_types: {e}"))?;
|
|
let types = frame_decode::legacy_types::from_bytes(&bytes)
|
|
.map_err(|e| eyre!("Cannot deserialize legacy_types: {e}"))?;
|
|
Ok::<_, color_eyre::eyre::Error>(types)
|
|
})
|
|
.transpose()?;
|
|
|
|
codegen(
|
|
&bytes,
|
|
legacy_types,
|
|
opts.legacy_spec_version,
|
|
opts.derives,
|
|
opts.attributes,
|
|
opts.derives_for_type,
|
|
opts.attributes_for_type,
|
|
opts.substitute_types,
|
|
opts.crate_path,
|
|
opts.no_docs,
|
|
opts.runtime_types_only,
|
|
opts.no_default_derives,
|
|
opts.no_default_substitutions,
|
|
output,
|
|
)?;
|
|
Ok(())
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
struct OuterAttribute(syn::Attribute);
|
|
|
|
impl syn::parse::Parse for OuterAttribute {
|
|
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
|
Ok(Self(input.call(syn::Attribute::parse_outer)?[0].clone()))
|
|
}
|
|
}
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
fn codegen(
|
|
metadata_bytes: &[u8],
|
|
legacy_types: Option<scale_info_legacy::ChainTypeRegistry>,
|
|
legacy_spec_version: Option<u64>,
|
|
raw_derives: Vec<String>,
|
|
raw_attributes: Vec<String>,
|
|
derives_for_type: Vec<DeriveForType>,
|
|
attributes_for_type: Vec<AttributeForType>,
|
|
substitute_types: Vec<(String, String)>,
|
|
crate_path: Option<String>,
|
|
no_docs: bool,
|
|
runtime_types_only: bool,
|
|
no_default_derives: bool,
|
|
no_default_substitutions: bool,
|
|
output: &mut impl std::io::Write,
|
|
) -> color_eyre::Result<()> {
|
|
let mut codegen = CodegenBuilder::new();
|
|
|
|
// 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()
|
|
}
|
|
|
|
let metadata = {
|
|
let runtime_metadata = subxt_metadata::decode_runtime_metadata(metadata_bytes)?;
|
|
let mut metadata = match runtime_metadata {
|
|
// Too old to work with:
|
|
frame_metadata::RuntimeMetadata::V0(_)
|
|
| frame_metadata::RuntimeMetadata::V1(_)
|
|
| frame_metadata::RuntimeMetadata::V2(_)
|
|
| frame_metadata::RuntimeMetadata::V3(_)
|
|
| frame_metadata::RuntimeMetadata::V4(_)
|
|
| frame_metadata::RuntimeMetadata::V5(_)
|
|
| frame_metadata::RuntimeMetadata::V6(_)
|
|
| frame_metadata::RuntimeMetadata::V7(_) => {
|
|
Err(eyre!("Metadata V1-V7 cannot be decoded from"))
|
|
}
|
|
// Converting legacy metadatas:
|
|
frame_metadata::RuntimeMetadata::V8(md) => {
|
|
let legacy_types = legacy_types
|
|
.ok_or_else(|| eyre!("--legacy-types needed to load V8 metadata"))?;
|
|
let legacy_spec = legacy_spec_version
|
|
.ok_or_else(|| eyre!("--legacy-spec-version needed to load V8 metadata"))?;
|
|
Metadata::from_v8(&md, &legacy_types.for_spec_version(legacy_spec))
|
|
.map_err(|e| eyre!("Cannot load V8 metadata: {e}"))
|
|
}
|
|
frame_metadata::RuntimeMetadata::V9(md) => {
|
|
let legacy_types = legacy_types
|
|
.ok_or_else(|| eyre!("--legacy-types needed to load V9 metadata"))?;
|
|
let legacy_spec = legacy_spec_version
|
|
.ok_or_else(|| eyre!("--legacy-spec-version needed to load V9 metadata"))?;
|
|
Metadata::from_v9(&md, &legacy_types.for_spec_version(legacy_spec))
|
|
.map_err(|e| eyre!("Cannot load V9 metadata: {e}"))
|
|
}
|
|
frame_metadata::RuntimeMetadata::V10(md) => {
|
|
let legacy_types = legacy_types
|
|
.ok_or_else(|| eyre!("--legacy-types needed to load V10 metadata"))?;
|
|
let legacy_spec = legacy_spec_version
|
|
.ok_or_else(|| eyre!("--legacy-spec-version needed to load V10 metadata"))?;
|
|
Metadata::from_v10(&md, &legacy_types.for_spec_version(legacy_spec))
|
|
.map_err(|e| eyre!("Cannot load V10 metadata: {e}"))
|
|
}
|
|
frame_metadata::RuntimeMetadata::V11(md) => {
|
|
let legacy_types = legacy_types
|
|
.ok_or_else(|| eyre!("--legacy-types needed to load V11 metadata"))?;
|
|
let legacy_spec = legacy_spec_version
|
|
.ok_or_else(|| eyre!("--legacy-spec-version needed to load V11 metadata"))?;
|
|
Metadata::from_v11(&md, &legacy_types.for_spec_version(legacy_spec))
|
|
.map_err(|e| eyre!("Cannot load V11 metadata: {e}"))
|
|
}
|
|
frame_metadata::RuntimeMetadata::V12(md) => {
|
|
let legacy_types = legacy_types
|
|
.ok_or_else(|| eyre!("--legacy-types needed to load V12 metadata"))?;
|
|
let legacy_spec = legacy_spec_version
|
|
.ok_or_else(|| eyre!("--legacy-spec-version needed to load V12 metadata"))?;
|
|
Metadata::from_v12(&md, &legacy_types.for_spec_version(legacy_spec))
|
|
.map_err(|e| eyre!("Cannot load V12 metadata: {e}"))
|
|
}
|
|
frame_metadata::RuntimeMetadata::V13(md) => {
|
|
let legacy_types = legacy_types
|
|
.ok_or_else(|| eyre!("--legacy-types needed to load V13 metadata"))?;
|
|
let legacy_spec = legacy_spec_version
|
|
.ok_or_else(|| eyre!("--legacy-spec-version needed to load V13 metadata"))?;
|
|
Metadata::from_v13(&md, &legacy_types.for_spec_version(legacy_spec))
|
|
.map_err(|e| eyre!("Cannot load V13 metadata: {e}"))
|
|
}
|
|
// Converting modern metadatas:
|
|
frame_metadata::RuntimeMetadata::V14(md) => {
|
|
Metadata::from_v14(md).map_err(|e| eyre!("Cannot load V14 metadata: {e}"))
|
|
}
|
|
frame_metadata::RuntimeMetadata::V15(md) => {
|
|
Metadata::from_v15(md).map_err(|e| eyre!("Cannot load V15 metadata: {e}"))
|
|
}
|
|
frame_metadata::RuntimeMetadata::V16(md) => {
|
|
Metadata::from_v16(md).map_err(|e| eyre!("Cannot load V16 metadata: {e}"))
|
|
}
|
|
}?;
|
|
|
|
// Run this first to ensure type paths are unique (which may result in 1,2,3 suffixes being added
|
|
// to type paths), so that when we validate derives/substitutions below, they are allowed for such
|
|
// types. See <https://github.com/paritytech/subxt/issues/2011>.
|
|
scale_typegen::utils::ensure_unique_type_paths(metadata.types_mut())
|
|
.expect("ensure_unique_type_paths should not fail; please report an issue.");
|
|
|
|
metadata
|
|
};
|
|
|
|
// Configure derives:
|
|
let global_derives = raw_derives
|
|
.iter()
|
|
.map(|raw| syn::parse_str(raw))
|
|
.collect::<Result<Vec<_>, _>>()
|
|
.map_err(|e| eyre!("Cannot parse global derives: {e}"))?;
|
|
codegen.set_additional_global_derives(global_derives);
|
|
|
|
for d in derives_for_type {
|
|
let ty_str = &d.type_path;
|
|
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(&d.trait_path)
|
|
.map_err(|e| eyre!("Cannot parse derive for type {ty_str}: {e}"))?;
|
|
|
|
validate_path_with_metadata(&ty.path, &metadata)?;
|
|
// Note: recursive derives and attributes not supported in the CLI => recursive: false
|
|
codegen.add_derives_for_type(ty, std::iter::once(derive), d.recursive);
|
|
}
|
|
|
|
// Configure attributes:
|
|
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<_>, _>>()
|
|
.map_err(|e| eyre!("Cannot parse global attributes: {e}"))?;
|
|
codegen.set_additional_global_attributes(universal_attributes);
|
|
|
|
for a in attributes_for_type {
|
|
let ty_str = &a.type_path;
|
|
let ty: syn::TypePath = syn::parse_str(ty_str)
|
|
.map_err(|e| eyre!("Cannot parse attribute for type {ty_str}: {e}"))?;
|
|
let attribute: OuterAttribute = syn::parse_str(&a.attribute)
|
|
.map_err(|e| eyre!("Cannot parse attribute for type {ty_str}: {e}"))?;
|
|
|
|
validate_path_with_metadata(&ty.path, &metadata)?;
|
|
// Note: recursive derives and attributes not supported in the CLI => recursive: false
|
|
codegen.add_attributes_for_type(ty, std::iter::once(attribute.0), a.recursive);
|
|
}
|
|
|
|
// Insert type substitutions:
|
|
for (from_str, to_str) in substitute_types {
|
|
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}"))?;
|
|
|
|
validate_path_with_metadata(&from, &metadata)?;
|
|
codegen.set_type_substitute(from, to);
|
|
}
|
|
|
|
let code = codegen
|
|
.generate(metadata)
|
|
.map_err(|e| eyre!("Cannot generate code: {e}"))?;
|
|
|
|
writeln!(output, "{code}")?;
|
|
Ok(())
|
|
}
|
|
|
|
/// Validates that the type path is part of the metadata.
|
|
fn validate_path_with_metadata(path: &syn::Path, metadata: &Metadata) -> color_eyre::Result<()> {
|
|
fn pretty_path(path: &syn::Path) -> String {
|
|
use quote::ToTokens;
|
|
path.to_token_stream().to_string().replace(' ', "")
|
|
}
|
|
|
|
let path_segments = path_segments(path);
|
|
let ident = &path
|
|
.segments
|
|
.last()
|
|
.expect("Empty path should be filtered out before already")
|
|
.ident;
|
|
if !registry_contains_type_path(metadata.types(), &path_segments) {
|
|
let alternatives = similar_type_paths_in_registry(metadata.types(), path);
|
|
let alternatives: String = if alternatives.is_empty() {
|
|
format!("There is no Type with name `{ident}` in the provided metadata.")
|
|
} else {
|
|
let mut s = "A type with the same name is present at: ".to_owned();
|
|
for p in alternatives {
|
|
s.push('\n');
|
|
s.push_str(&pretty_path(&p));
|
|
}
|
|
s
|
|
};
|
|
|
|
color_eyre::eyre::bail!(
|
|
"Type `{}` does not exist at path `{}`\n{}",
|
|
ident.to_string(),
|
|
pretty_path(path),
|
|
alternatives
|
|
);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
|
|
#[test]
|
|
fn parse_types() {
|
|
use crate::commands::codegen::type_map_parser;
|
|
|
|
assert_eq!(type_map_parser("Foo"), None);
|
|
assert_eq!(type_map_parser("Foo=Bar"), Some(("Foo", "Bar", false)));
|
|
assert_eq!(
|
|
type_map_parser("Foo=Bar,recursive"),
|
|
Some(("Foo", "Bar", true))
|
|
);
|
|
assert_eq!(type_map_parser("Foo=Bar,a"), None);
|
|
assert_eq!(type_map_parser("Foo=Bar,a,b,c,recursive"), None);
|
|
}
|
|
|
|
async fn run(args_str: &str) -> color_eyre::Result<String> {
|
|
let mut args = vec![
|
|
"codegen",
|
|
"--file=../artifacts/polkadot_metadata_small.scale",
|
|
];
|
|
args.extend(args_str.split(' ').filter(|e| !e.is_empty()));
|
|
let opts: super::Opts = clap::Parser::try_parse_from(args)?;
|
|
let mut output: Vec<u8> = Vec::new();
|
|
let r = super::run(opts, &mut output)
|
|
.await
|
|
.map(|_| String::from_utf8(output).unwrap())?;
|
|
Ok(r)
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn invalid_type_paths() {
|
|
let valid_type = "sp_runtime::multiaddress::MultiAddress";
|
|
let invalid_type = "my_module::MultiAddress";
|
|
|
|
let valid_cases = [
|
|
format!("--derive-for-type {valid_type}=serde::Serialize"),
|
|
format!("--attributes-for-type {valid_type}=#[allow(clippy::all)]"),
|
|
format!("--substitute-type {valid_type}=::my_crate::MultiAddress"),
|
|
];
|
|
for case in valid_cases.iter() {
|
|
let output = run(case).await;
|
|
assert!(output.is_ok());
|
|
}
|
|
|
|
let invalid_cases = [
|
|
format!("--derive-for-type {invalid_type}=serde::Serialize"),
|
|
format!("--attributes-for-type {invalid_type}=#[allow(clippy::all)]"),
|
|
format!("--substitute-type {invalid_type}=my_module::MultiAddress"),
|
|
];
|
|
for case in invalid_cases.iter() {
|
|
let output = run(case).await;
|
|
// assert that we make suggestions pointing the user to the valid type
|
|
assert!(output.unwrap_err().to_string().contains(valid_type));
|
|
}
|
|
}
|
|
}
|