Skip to content
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
84b3bb4
Don't use a feature in the derive crate
dvdplm Mar 3, 2021
c2f0f06
Review grumbles
dvdplm Mar 3, 2021
b4ba154
Less noise is better: build a TokenStream2 containing the "::" prefix
dvdplm Mar 3, 2021
b1d26c2
Use `crate::` when deriving for `scale-info` itself
dvdplm Mar 3, 2021
22fb440
Derive TypeInfo for UntrackedSymbol
dvdplm Mar 4, 2021
00aae34
wip
dvdplm Mar 4, 2021
5a165b1
Add ref to ticket that blocks deriving Typeinfo for Type
dvdplm Mar 4, 2021
9ac6306
Remove TypeInfo impl for MetaType
dvdplm Mar 4, 2021
39f8459
Clippy is right
dvdplm Mar 4, 2021
7db6778
Cleanup
dvdplm Mar 4, 2021
687c757
Merge branch 'master' into dp-doogfood
dvdplm Mar 4, 2021
7f4fae7
Merge branch 'master' into dp-doogfood
dvdplm Mar 5, 2021
1d8ff8c
Merge branch 'master' into dp-doogfood
dvdplm Jun 25, 2021
5c22f19
Remove `NonZero*` impls
dvdplm Jun 25, 2021
58dc3d2
Cleanup
dvdplm Jun 25, 2021
e73c061
Don't bind <… as HasCompact>::Type
dvdplm Jun 25, 2021
03bf034
Merge remote-tracking branch 'origin/master' into dp-doogfood
dvdplm Jun 28, 2021
c7bfb52
Review grumbles
dvdplm Jun 28, 2021
89d4499
Fix comment
dvdplm Jun 29, 2021
14055e1
Merge branch 'master' into dp-doogfood
dvdplm Jun 29, 2021
c7c19fc
Address review feedback
dvdplm Jun 29, 2021
d3259c5
fmt
dvdplm Jun 29, 2021
54deaf4
Merge branch 'master' into dp-doogfood
dvdplm Aug 24, 2021
5fd7525
cleanup
dvdplm Aug 24, 2021
2d7530d
Merge branch 'master' into dp-doogfood
gilescope Jul 16, 2022
ba2e94a
Fixup after merge
gilescope Jul 16, 2022
3420903
additional support to allow frame-metadata to derive TypeInfo (#163)
gilescope Jul 19, 2022
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
1 change: 1 addition & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ jobs:
- name: check-features
run: |
cargo check --no-default-features --features bit-vec
cargo check --no-default-features --features dogfood
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to keep this optional crate feature? What purpose does it have?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a good question. I fail to see the reason to keep this as a feature. @ascjones you ok making it non-optional?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just saw Giles's PR with this stuff in and fwiw I think it doesn't need to be behind a feature flag.

cargo check --no-default-features --features docs
cargo check --no-default-features --features serde
cargo check --no-default-features --features serde,decode
Expand Down
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ std = [
derive = [
"scale-info-derive"
]
# Derive `TypeInfo` for our own types.
dogfood = [
"derive"
]
# Enables decoding and deserialization of portable scale-info type metadata.
# Include rustdoc strings in the type metadata.
docs = [
"scale-info-derive/docs"
Expand Down
66 changes: 46 additions & 20 deletions derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ fn generate_type(input: TokenStream2) -> Result<TokenStream2> {

utils::check_attributes(&ast)?;

let scale_info = crate_name_ident("scale-info")?;
let scale_info = scale_info_crate_tokens()?;
let parity_scale_codec = crate_name_ident("parity-scale-codec")?;

let ident = &ast.ident;
Expand All @@ -91,11 +91,10 @@ fn generate_type(input: TokenStream2) -> Result<TokenStream2> {
};

let (impl_generics, ty_generics, _) = ast.generics.split_for_impl();

let generic_type_ids = ast.generics.type_params().map(|ty| {
let ty_ident = &ty.ident;
quote! {
:: #scale_info ::meta_type::<#ty_ident>()
#scale_info::meta_type::<#ty_ident>()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as before for all non-scale-info crates this should still prepend :: to signal root crate path.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't compile with the :: and I don't know why tbh, but I think that when deriving for scale-info itself, and we have extern crate self as _scale_info;, then ::_scale_info::path::to::something doesn't work. :/

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The extern crate self as _scale_info; is not required since Rust edition 2018 and I don't think we should support Rust edition 2015 tbh because it actually resolves many problems we are facing otherwise such as this one.

Copy link
Contributor

@Robbepop Robbepop Mar 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So either this crate should produce: crate:: paths or whatever crate alias a dependency uses for scale-info prepended with ::. To be honest I am not sure how counterintuitive it is to use the derive macros for the crate that exposes them. I see a gain but issues like these let me feel it might be better to simply provide manual implementations for the few types in the re-exporting crate. Happy to learn a better approaches for this problem. In ink! we also use manual implementations for our derive macros in the root crates.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be more concrete: While the proc-macro-crate crate is actually useful in these cases it makes use of file I/O while expanding a proc. macro. There are discussions about encapsulating proc. macros in stricter environments that would break proc. macros such as these. Generally tooling such as rust-analyzer has good reasons why proc. macros that are "impure" should not be relied on.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be honest I am not sure how counterintuitive it is to use the derive macros for the crate that exposes them.

How do you mean "counterintuitive"? I take it you mean it's forcing things a bit?

I think it's a good litmus test for the crate, especially since the type zoo in scale-info is fairly advanced and gives us a "free test suite" of sorts. The amount of manual implementations we'd have to maintain is pretty large so if we can derive most of them that's a win imo.

I am concerned by the increased complexity this brings. I will see what I can do to make things a wee bit less messy.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am concerned by the increased complexity this brings. I will see what I can do to make things a wee bit less messy.

I think this commit is makes things much cleaner.

Copy link
Contributor

@Robbepop Robbepop Mar 4, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Proc. macros bring overhead with them. So some people prefer using crates but not their provided proc. macros which is why we find derive crate feature on some crates to opt-out. It is just nice to be able to separate concerns between the crate that provides definitions and the crate that provides the proc. macro implementations. As soon as the crate providing definitions makes use of the proc. macro you no longer have the separation and a direct dependency instead. I personally am not against this but overhead can be serious in some cases.
The biggest problem is that derive macros that can be used today in their parent crate require this type of "hack" that is encapsulated by the proc-macro-crate crate currently. Using this "hack" in any proc. macro turns the proc. macro impure. For example it can no longer enjoy acceleration by the techniques behind the watt crate which some people believe to be the future compilation model for all proc. macros since it is super fast and more secure than today's proc. macros.

Don't get me wrong: I see the tradeoffs and see some of the pros of doing this. But I still consider this feature to be taking in some amount of technical depth into the project in order to have the anticipated feature implementation for which we will probably have to pay back in the future. Technical depth is death to all progress so I always am careful how much of it I accumulate in projects.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. The use of proc-macro-crate predates this PR so the technical debt is already there;
  2. This PR does not introduce a mandatory dependence on scale-info-derive: it's opt-in through a feature (as it should be, couldn't agree with you more);

I think we should have a conversation about the use of proc-macro-crate; you make a really good point about it making the crate "impure" and we should hash out the trade-offs involved. But I'm not sure this PR is the right place for that?

}
});

Expand All @@ -107,22 +106,21 @@ fn generate_type(input: TokenStream2) -> Result<TokenStream2> {
let docs = generate_docs(&ast.attrs);

let type_info_impl = quote! {
impl #impl_generics :: #scale_info ::TypeInfo for #ident #ty_generics #where_clause {
impl #impl_generics #scale_info ::TypeInfo for #ident #ty_generics #where_clause {
type Identity = Self;
fn type_info() -> :: #scale_info ::Type {
:: #scale_info ::Type::builder()
.path(:: #scale_info ::Path::new(::core::stringify!(#ident), ::core::module_path!()))
.type_params(:: #scale_info ::prelude::vec![ #( #generic_type_ids ),* ])
fn type_info() -> #scale_info::Type {
#scale_info::Type::builder()
.path(#scale_info::Path::new(::core::stringify!(#ident), ::core::module_path!()))
.type_params(#scale_info::prelude::vec![ #( #generic_type_ids ),* ])
#docs
.#build_type
}
}
};
};

Ok(quote! {
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
const _: () = {
#type_info_impl;
#type_info_impl
};
})
}
Expand All @@ -140,6 +138,25 @@ fn crate_name_ident(name: &str) -> Result<Ident> {
.map_err(|e| syn::Error::new(Span::call_site(), &e))
}

/// Find the name given to the `scale-info` crate in the context we run in.
/// If scale-info is not among the dependencies then we must be deriving
/// types for the scale-info crate itself, in which case we need to rename
/// "self" to something, so the object paths keep working.
fn scale_info_crate_tokens() -> Result<TokenStream2> {
use proc_macro_crate::FoundCrate;
const SCALE_INFO_CRATE_NAME: &str = "scale-info";

let crate_ident = match proc_macro_crate::crate_name(SCALE_INFO_CRATE_NAME) {
Ok(FoundCrate::Itself) => quote! { crate },
Ok(FoundCrate::Name(name)) => {
let ident = Ident::new(&name, Span::call_site());
quote! { :: #ident }
}
Err(e) => return Err(syn::Error::new(Span::call_site(), e)),
};
Ok(crate_ident)
}

type FieldsList = Punctuated<Field, Comma>;

fn generate_fields(fields: &FieldsList) -> Vec<TokenStream2> {
Expand Down Expand Up @@ -204,7 +221,10 @@ fn clean_type_string(input: &str) -> String {
.replace("&\'", "&'")
}

fn generate_composite_type(data_struct: &DataStruct, scale_info: &Ident) -> TokenStream2 {
fn generate_composite_type(
data_struct: &DataStruct,
scale_info: &TokenStream2,
) -> TokenStream2 {
let fields = match data_struct.fields {
Fields::Named(ref fs) => {
let fields = generate_fields(&fs.named);
Expand All @@ -221,13 +241,16 @@ fn generate_composite_type(data_struct: &DataStruct, scale_info: &Ident) -> Toke
}
};
quote! {
composite(:: #scale_info ::build::Fields::#fields)
composite(#scale_info::build::Fields::#fields)
}
}

type VariantList = Punctuated<Variant, Comma>;

fn generate_c_like_enum_def(variants: &VariantList, scale_info: &Ident) -> TokenStream2 {
fn generate_c_like_enum_def(
variants: &VariantList,
scale_info: &TokenStream2,
) -> TokenStream2 {
let variants = variants
.into_iter()
.enumerate()
Expand All @@ -246,7 +269,7 @@ fn generate_c_like_enum_def(variants: &VariantList, scale_info: &Ident) -> Token
});
quote! {
variant(
:: #scale_info ::build::Variants::new()
#scale_info::build::Variants::new()
#( #variants )*
)
}
Expand All @@ -259,7 +282,10 @@ fn is_c_like_enum(variants: &VariantList) -> bool {
variants.iter().all(|v| matches!(v.fields, Fields::Unit))
}

fn generate_variant_type(data_enum: &DataEnum, scale_info: &Ident) -> TokenStream2 {
fn generate_variant_type(
data_enum: &DataEnum,
scale_info: &TokenStream2,
) -> TokenStream2 {
let variants = &data_enum.variants;

if is_c_like_enum(variants) {
Expand All @@ -279,20 +305,20 @@ fn generate_variant_type(data_enum: &DataEnum, scale_info: &Ident) -> TokenStrea
Fields::Named(ref fs) => {
let fields = generate_fields(&fs.named);
quote! {
:: #scale_info::build::Fields::named()
#scale_info::build::Fields::named()
#( #fields )*
}
}
Fields::Unnamed(ref fs) => {
let fields = generate_fields(&fs.unnamed);
quote! {
:: #scale_info::build::Fields::unnamed()
#scale_info::build::Fields::unnamed()
#( #fields )*
}
}
Fields::Unit => {
quote! {
:: #scale_info::build::Fields::unit()
#scale_info::build::Fields::unit()
}
}
};
Expand All @@ -308,7 +334,7 @@ fn generate_variant_type(data_enum: &DataEnum, scale_info: &Ident) -> TokenStrea
});
quote! {
variant(
:: #scale_info ::build::Variants::new()
#scale_info ::build::Variants::new()
#( #variants )*
)
}
Expand Down
13 changes: 8 additions & 5 deletions derive/src/trait_bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
// limitations under the License.

use alloc::vec::Vec;
use proc_macro2::Ident;
use proc_macro2::{
Ident,
TokenStream,
};
use syn::{
parse_quote,
punctuated::Punctuated,
Expand All @@ -38,7 +41,7 @@ pub fn make_where_clause<'a>(
input_ident: &'a Ident,
generics: &'a Generics,
data: &'a syn::Data,
scale_info: &Ident,
scale_info: &TokenStream,
parity_scale_codec: &Ident,
) -> Result<WhereClause> {
let mut where_clause = generics.where_clause.clone().unwrap_or_else(|| {
Expand Down Expand Up @@ -68,18 +71,18 @@ pub fn make_where_clause<'a>(
if is_compact {
where_clause
.predicates
.push(parse_quote!(#ty : :: #parity_scale_codec ::HasCompact));
.push(parse_quote!(#ty : #parity_scale_codec ::HasCompact));
} else {
where_clause
.predicates
.push(parse_quote!(#ty : :: #scale_info ::TypeInfo + 'static));
.push(parse_quote!(#ty : #scale_info ::TypeInfo + 'static));
}
});

generics.type_params().into_iter().for_each(|type_param| {
let ident = type_param.ident.clone();
let mut bounds = type_param.bounds.clone();
bounds.push(parse_quote!(:: #scale_info ::TypeInfo));
bounds.push(parse_quote!(#scale_info ::TypeInfo));
bounds.push(parse_quote!('static));
where_clause
.predicates
Expand Down
1 change: 1 addition & 0 deletions src/form.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ pub trait Form {
/// through the registry and `IntoPortable`.
#[cfg_attr(feature = "serde", derive(Serialize))]
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)]
#[cfg_attr(feature = "dogfood", derive(scale_info_derive::TypeInfo))]
pub enum MetaForm {}

impl Form for MetaForm {
Expand Down
1 change: 1 addition & 0 deletions src/interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ use serde::{
)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
#[cfg_attr(feature = "dogfood", derive(scale_info_derive::TypeInfo))]
pub struct UntrackedSymbol<T> {
/// The index to the symbol in the interner table.
id: u32,
Expand Down
1 change: 1 addition & 0 deletions src/ty/composite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ use serde::{
#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
#[cfg_attr(any(feature = "std", feature = "decode"), derive(scale::Decode))]
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug, From, Encode)]
#[cfg_attr(feature = "dogfood", derive(scale_info_derive::TypeInfo))]
pub struct TypeDefComposite<T: Form = MetaForm> {
/// The fields of the composite type.
#[cfg_attr(
Expand Down
1 change: 1 addition & 0 deletions src/ty/fields.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ use serde::{
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[cfg_attr(any(feature = "std", feature = "decode"), derive(scale::Decode))]
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug, Encode)]
#[cfg_attr(feature = "dogfood", derive(scale_info_derive::TypeInfo))]
pub struct Field<T: Form = MetaForm> {
/// The name of the field. None for unnamed fields.
#[cfg_attr(
Expand Down
10 changes: 9 additions & 1 deletion src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ pub use self::{

/// A [`Type`] definition with optional metadata.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "dogfood", derive(scale_info_derive::TypeInfo))]
#[cfg_attr(
feature = "serde",
serde(bound(
Expand Down Expand Up @@ -120,7 +121,7 @@ impl_from_type_def_for_type!(
);

impl Type {
/// Create a [`TypeBuilder`](`crate::build::TypeBuilder`) the public API for constructing a [`Type`]
/// Create a [`TypeBuilder`](`crate::build::TypeBuilder`), the public API for constructing a [`Type`]
pub fn builder() -> TypeBuilder {
TypeBuilder::default()
}
Expand Down Expand Up @@ -181,6 +182,7 @@ where
#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
#[cfg_attr(any(feature = "std", feature = "decode"), derive(scale::Decode))]
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, From, Debug, Encode)]
#[cfg_attr(feature = "dogfood", derive(scale_info_derive::TypeInfo))]
pub enum TypeDef<T: Form = MetaForm> {
/// A composite type (e.g. a struct or a tuple)
Composite(TypeDefComposite<T>),
Expand Down Expand Up @@ -225,6 +227,7 @@ impl IntoPortable for TypeDef {
#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
#[cfg_attr(any(feature = "std", feature = "decode"), derive(scale::Decode))]
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Debug)]
#[cfg_attr(feature = "dogfood", derive(scale_info_derive::TypeInfo))]
pub enum TypeDefPrimitive {
/// `bool` type
Bool,
Expand Down Expand Up @@ -262,6 +265,7 @@ pub enum TypeDefPrimitive {
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(any(feature = "std", feature = "decode"), derive(scale::Decode))]
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Debug)]
#[cfg_attr(feature = "dogfood", derive(scale_info_derive::TypeInfo))]
pub struct TypeDefArray<T: Form = MetaForm> {
/// The length of the array type.
len: u32,
Expand Down Expand Up @@ -316,6 +320,7 @@ where
#[cfg_attr(feature = "serde", serde(transparent))]
#[cfg_attr(any(feature = "std", feature = "decode"), derive(scale::Decode))]
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Debug)]
#[cfg_attr(feature = "dogfood", derive(scale_info_derive::TypeInfo))]
pub struct TypeDefTuple<T: Form = MetaForm> {
/// The types of the tuple fields.
fields: Vec<T::Type>,
Expand Down Expand Up @@ -362,6 +367,7 @@ where
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(any(feature = "std", feature = "decode"), derive(scale::Decode))]
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Debug)]
#[cfg_attr(feature = "dogfood", derive(scale_info_derive::TypeInfo))]
pub struct TypeDefSequence<T: Form = MetaForm> {
/// The element type of the sequence type.
#[cfg_attr(feature = "serde", serde(rename = "type"))]
Expand Down Expand Up @@ -412,6 +418,7 @@ where
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(any(feature = "std", feature = "decode"), derive(scale::Decode))]
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Debug)]
#[cfg_attr(feature = "dogfood", derive(scale_info_derive::TypeInfo))]
pub struct TypeDefCompact<T: Form = MetaForm> {
/// The type wrapped in [`Compact`], i.e. the `T` in `Compact<T>`.
#[cfg_attr(feature = "serde", serde(rename = "type"))]
Expand Down Expand Up @@ -457,6 +464,7 @@ where
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(any(feature = "std", feature = "decode"), derive(scale::Decode))]
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Debug)]
#[cfg_attr(feature = "dogfood", derive(scale_info_derive::TypeInfo))]
pub struct TypeDefPhantom<T: Form = MetaForm> {
/// The PhantomData type parameter
#[cfg_attr(feature = "serde", serde(rename = "type"))]
Expand Down
1 change: 1 addition & 0 deletions src/ty/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ use serde::{
#[cfg_attr(feature = "serde", serde(transparent))]
#[cfg_attr(any(feature = "std", feature = "decode"), derive(scale::Decode))]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Encode)]
#[cfg_attr(feature = "dogfood", derive(scale_info_derive::TypeInfo))]
pub struct Path<T: Form = MetaForm> {
/// The segments of the namespace.
segments: Vec<T::String>,
Expand Down
2 changes: 2 additions & 0 deletions src/ty/variant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ use serde::{
#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
#[cfg_attr(any(feature = "std", feature = "decode"), derive(scale::Decode))]
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug, From, Encode)]
#[cfg_attr(feature = "dogfood", derive(scale_info_derive::TypeInfo))]
pub struct TypeDefVariant<T: Form = MetaForm> {
/// The variants of a variant type
#[cfg_attr(
Expand Down Expand Up @@ -148,6 +149,7 @@ where
)]
#[cfg_attr(any(feature = "std", feature = "decode"), derive(scale::Decode))]
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug, Encode)]
#[cfg_attr(feature = "dogfood", derive(scale_info_derive::TypeInfo))]
pub struct Variant<T: Form = MetaForm> {
/// The name of the variant.
name: T::String,
Expand Down