diff --git a/codegen/src/api/calls.rs b/codegen/src/api/calls.rs
index 0b2dcad341..25633ff4dc 100644
--- a/codegen/src/api/calls.rs
+++ b/codegen/src/api/calls.rs
@@ -14,7 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with subxt. If not, see .
-use crate::types::TypeGenerator;
+use crate::types::{
+ CompositeDefFields,
+ TypeGenerator,
+};
use frame_metadata::{
PalletCallMetadata,
PalletMetadata,
@@ -39,17 +42,29 @@ pub fn generate_calls(
let (call_structs, call_fns): (Vec<_>, Vec<_>) = struct_defs
.iter()
.map(|struct_def| {
- let (call_fn_args, call_args): (Vec<_>, Vec<_>) = struct_def
- .named_fields()
- .unwrap_or_else(|| {
- abort_call_site!(
- "Call variant for type {} must have all named fields",
- call.ty.id()
- )
- })
- .iter()
- .map(|(name, ty)| (quote!( #name: #ty ), name))
- .unzip();
+ let (call_fn_args, call_args): (Vec<_>, Vec<_>) =
+ match struct_def.fields {
+ CompositeDefFields::Named(ref named_fields) => {
+ named_fields
+ .iter()
+ .map(|(name, field)| {
+ let fn_arg_type = &field.type_path;
+ let call_arg = if field.is_boxed() {
+ quote! { #name: ::std::boxed::Box::new(#name) }
+ } else {
+ quote! { #name }
+ };
+ (quote!( #name: #fn_arg_type ), call_arg)
+ })
+ .unzip()
+ }
+ CompositeDefFields::NoFields => Default::default(),
+ CompositeDefFields::Unnamed(_) =>
+ abort_call_site!(
+ "Call variant for type {} must have all named fields",
+ call.ty.id()
+ )
+ };
let pallet_name = &pallet.name;
let call_struct_name = &struct_def.name;
diff --git a/codegen/src/api/mod.rs b/codegen/src/api/mod.rs
index ea8e73c45f..e7d317f6d1 100644
--- a/codegen/src/api/mod.rs
+++ b/codegen/src/api/mod.rs
@@ -22,8 +22,11 @@ mod storage;
use super::GeneratedTypeDerives;
use crate::{
ir,
- struct_def::StructDef,
- types::TypeGenerator,
+ types::{
+ CompositeDef,
+ CompositeDefFields,
+ TypeGenerator,
+ },
};
use codec::Decode;
use frame_metadata::{
@@ -316,21 +319,28 @@ impl RuntimeGenerator {
}
}
-pub fn generate_structs_from_variants(
- type_gen: &TypeGenerator,
+pub fn generate_structs_from_variants<'a>(
+ type_gen: &'a TypeGenerator,
type_id: u32,
error_message_type_name: &str,
-) -> Vec {
+) -> Vec {
let ty = type_gen.resolve_type(type_id);
if let scale_info::TypeDef::Variant(variant) = ty.type_def() {
variant
.variants()
.iter()
.map(|var| {
- StructDef::new(
+ let fields = CompositeDefFields::from_scale_info_fields(
var.name(),
var.fields(),
- Some(syn::parse_quote!(pub)),
+ &[],
+ type_gen,
+ );
+ CompositeDef::struct_def(
+ var.name(),
+ Default::default(),
+ fields,
+ Some(parse_quote!(pub)),
type_gen,
)
})
diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs
index bf38353c7e..e65dc26195 100644
--- a/codegen/src/lib.rs
+++ b/codegen/src/lib.rs
@@ -17,9 +17,7 @@
//! Library to generate an API for a Substrate runtime from its metadata.
mod api;
-mod derives;
mod ir;
-mod struct_def;
mod types;
pub use self::{
@@ -27,5 +25,5 @@ pub use self::{
generate_runtime_api,
RuntimeGenerator,
},
- derives::GeneratedTypeDerives,
+ types::GeneratedTypeDerives,
};
diff --git a/codegen/src/struct_def.rs b/codegen/src/struct_def.rs
deleted file mode 100644
index 5dc18c429e..0000000000
--- a/codegen/src/struct_def.rs
+++ /dev/null
@@ -1,142 +0,0 @@
-// Copyright 2019-2022 Parity Technologies (UK) Ltd.
-// This file is part of subxt.
-//
-// subxt is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// subxt is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with subxt. If not, see .
-
-use super::GeneratedTypeDerives;
-use crate::types::{
- TypeGenerator,
- TypePath,
-};
-use heck::CamelCase as _;
-use proc_macro2::TokenStream as TokenStream2;
-use proc_macro_error::abort_call_site;
-use quote::{
- format_ident,
- quote,
-};
-use scale_info::form::PortableForm;
-
-#[derive(Debug)]
-pub struct StructDef {
- pub name: syn::Ident,
- pub fields: StructDefFields,
- pub field_visibility: Option,
- pub derives: GeneratedTypeDerives,
-}
-
-#[derive(Debug)]
-pub enum StructDefFields {
- Named(Vec<(syn::Ident, TypePath)>),
- Unnamed(Vec),
-}
-
-impl StructDef {
- pub fn new(
- ident: &str,
- fields: &[scale_info::Field],
- field_visibility: Option,
- type_gen: &TypeGenerator,
- ) -> Self {
- let name = format_ident!("{}", ident.to_camel_case());
- let fields = fields
- .iter()
- .map(|field| {
- let name = field.name().map(|f| format_ident!("{}", f));
- let ty = type_gen.resolve_type_path(field.ty().id(), &[]);
- (name, ty)
- })
- .collect::>();
-
- let named = fields.iter().all(|(name, _)| name.is_some());
- let unnamed = fields.iter().all(|(name, _)| name.is_none());
-
- let fields = if named {
- StructDefFields::Named(
- fields
- .iter()
- .map(|(name, field)| {
- let name = name.as_ref().unwrap_or_else(|| {
- abort_call_site!("All fields should have a name")
- });
- (name.clone(), field.clone())
- })
- .collect(),
- )
- } else if unnamed {
- StructDefFields::Unnamed(
- fields.iter().map(|(_, field)| field.clone()).collect(),
- )
- } else {
- abort_call_site!(
- "Struct '{}': Fields should either be all named or all unnamed.",
- name,
- )
- };
-
- let derives = type_gen.derives().clone();
-
- Self {
- name,
- fields,
- field_visibility,
- derives,
- }
- }
-
- pub fn named_fields(&self) -> Option<&[(syn::Ident, TypePath)]> {
- if let StructDefFields::Named(ref fields) = self.fields {
- Some(fields)
- } else {
- None
- }
- }
-}
-
-impl quote::ToTokens for StructDef {
- fn to_tokens(&self, tokens: &mut TokenStream2) {
- let visibility = &self.field_visibility;
- let derives = &self.derives;
- tokens.extend(match self.fields {
- StructDefFields::Named(ref named_fields) => {
- let fields = named_fields.iter().map(|(name, ty)| {
- let compact_attr =
- ty.is_compact().then(|| quote!( #[codec(compact)] ));
- quote! { #compact_attr #visibility #name: #ty }
- });
- let name = &self.name;
- quote! {
- #derives
- pub struct #name {
- #( #fields ),*
- }
- }
- }
- StructDefFields::Unnamed(ref unnamed_fields) => {
- let fields = unnamed_fields.iter().map(|ty| {
- let compact_attr =
- ty.is_compact().then(|| quote!( #[codec(compact)] ));
- quote! { #compact_attr #visibility #ty }
- });
- let name = &self.name;
- quote! {
- #derives
- pub struct #name (
- #( #fields ),*
- );
- }
- }
- })
- }
-}
diff --git a/codegen/src/types/composite_def.rs b/codegen/src/types/composite_def.rs
new file mode 100644
index 0000000000..b6e5548f06
--- /dev/null
+++ b/codegen/src/types/composite_def.rs
@@ -0,0 +1,346 @@
+// Copyright 2019-2022 Parity Technologies (UK) Ltd.
+// This file is part of subxt.
+//
+// subxt is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// subxt is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with subxt. If not, see .
+
+use super::{
+ Field,
+ GeneratedTypeDerives,
+ TypeDefParameters,
+ TypeGenerator,
+ TypeParameter,
+ TypePath,
+};
+use heck::CamelCase as _;
+use proc_macro2::TokenStream;
+use proc_macro_error::abort_call_site;
+use quote::{
+ format_ident,
+ quote,
+};
+use scale_info::{
+ TypeDef,
+ TypeDefPrimitive,
+};
+
+/// Representation of a type which consists of a set of fields. Used to generate Rust code for
+/// either a standalone `struct` definition, or an `enum` variant.
+///
+/// Fields can either be named or unnamed in either case.
+#[derive(Debug)]
+pub struct CompositeDef {
+ /// The name of the `struct`, or the name of the `enum` variant.
+ pub name: syn::Ident,
+ /// Generate either a standalone `struct` or an `enum` variant.
+ pub kind: CompositeDefKind,
+ /// The fields of the type, which are either all named or all unnamed.
+ pub fields: CompositeDefFields,
+}
+
+impl CompositeDef {
+ /// Construct a definition which will generate code for a standalone `struct`.
+ pub fn struct_def(
+ ident: &str,
+ type_params: TypeDefParameters,
+ fields_def: CompositeDefFields,
+ field_visibility: Option,
+ type_gen: &TypeGenerator,
+ ) -> Self {
+ let mut derives = type_gen.derives().clone();
+ let fields: Vec<_> = fields_def.field_types().collect();
+
+ if fields.len() == 1 {
+ // any single field wrapper struct with a concrete unsigned int type can derive
+ // CompactAs.
+ let field = &fields[0];
+ if !type_params
+ .params()
+ .iter()
+ .any(|tp| Some(tp.original_name.to_string()) == field.type_name)
+ {
+ let ty = type_gen.resolve_type(field.type_id);
+ if matches!(
+ ty.type_def(),
+ TypeDef::Primitive(
+ TypeDefPrimitive::U8
+ | TypeDefPrimitive::U16
+ | TypeDefPrimitive::U32
+ | TypeDefPrimitive::U64
+ | TypeDefPrimitive::U128
+ )
+ ) {
+ derives.push_codec_compact_as()
+ }
+ }
+ }
+
+ let name = format_ident!("{}", ident.to_camel_case());
+
+ Self {
+ name,
+ kind: CompositeDefKind::Struct {
+ derives,
+ type_params,
+ field_visibility,
+ },
+ fields: fields_def,
+ }
+ }
+
+ /// Construct a definition which will generate code for an `enum` variant.
+ pub fn enum_variant_def(ident: &str, fields: CompositeDefFields) -> Self {
+ let name = format_ident!("{}", ident);
+ Self {
+ name,
+ kind: CompositeDefKind::EnumVariant,
+ fields,
+ }
+ }
+}
+
+impl quote::ToTokens for CompositeDef {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let name = &self.name;
+
+ let decl = match &self.kind {
+ CompositeDefKind::Struct {
+ derives,
+ type_params,
+ field_visibility,
+ } => {
+ let phantom_data = type_params.unused_params_phantom_data();
+ let fields = self
+ .fields
+ .to_struct_field_tokens(phantom_data, field_visibility.as_ref());
+ let trailing_semicolon = matches!(
+ self.fields,
+ CompositeDefFields::NoFields | CompositeDefFields::Unnamed(_)
+ )
+ .then(|| quote!(;));
+
+ quote! {
+ #derives
+ pub struct #name #type_params #fields #trailing_semicolon
+ }
+ }
+ CompositeDefKind::EnumVariant => {
+ let fields = self.fields.to_enum_variant_field_tokens();
+
+ quote! {
+ #name #fields
+ }
+ }
+ };
+ tokens.extend(decl)
+ }
+}
+
+/// Which kind of composite type are we generating, either a standalone `struct` or an `enum`
+/// variant.
+#[derive(Debug)]
+pub enum CompositeDefKind {
+ /// Composite type comprising a Rust `struct`.
+ Struct {
+ derives: GeneratedTypeDerives,
+ type_params: TypeDefParameters,
+ field_visibility: Option,
+ },
+ /// Comprises a variant of a Rust `enum`.
+ EnumVariant,
+}
+
+/// Encapsulates the composite fields, keeping the invariant that all fields are either named or
+/// unnamed.
+#[derive(Debug)]
+pub enum CompositeDefFields {
+ NoFields,
+ Named(Vec<(syn::Ident, CompositeDefFieldType)>),
+ Unnamed(Vec),
+}
+
+impl CompositeDefFields {
+ /// Construct a new set of composite fields from the supplied [`::scale_info::Field`]s.
+ pub fn from_scale_info_fields(
+ name: &str,
+ fields: &[Field],
+ parent_type_params: &[TypeParameter],
+ type_gen: &TypeGenerator,
+ ) -> Self {
+ if fields.is_empty() {
+ return Self::NoFields
+ }
+
+ let mut named_fields = Vec::new();
+ let mut unnamed_fields = Vec::new();
+
+ for field in fields {
+ let type_path =
+ type_gen.resolve_type_path(field.ty().id(), parent_type_params);
+ let field_type = CompositeDefFieldType::new(
+ field.ty().id(),
+ type_path,
+ field.type_name().cloned(),
+ );
+
+ if let Some(name) = field.name() {
+ let field_name = format_ident!("{}", name);
+ named_fields.push((field_name, field_type))
+ } else {
+ unnamed_fields.push(field_type)
+ }
+ }
+
+ if !named_fields.is_empty() && !unnamed_fields.is_empty() {
+ abort_call_site!(
+ "'{}': Fields should either be all named or all unnamed.",
+ name,
+ )
+ }
+
+ if !named_fields.is_empty() {
+ Self::Named(named_fields)
+ } else {
+ Self::Unnamed(unnamed_fields)
+ }
+ }
+
+ /// Returns the set of composite fields.
+ pub fn field_types(&self) -> Box + '_> {
+ match self {
+ Self::NoFields => Box::new([].iter()),
+ Self::Named(named_fields) => Box::new(named_fields.iter().map(|(_, f)| f)),
+ Self::Unnamed(unnamed_fields) => Box::new(unnamed_fields.iter()),
+ }
+ }
+
+ /// Generate the code for fields which will compose a `struct`.
+ pub fn to_struct_field_tokens(
+ &self,
+ phantom_data: Option,
+ visibility: Option<&syn::Visibility>,
+ ) -> TokenStream {
+ match self {
+ Self::NoFields => {
+ if let Some(phantom_data) = phantom_data {
+ quote! { ( #phantom_data ) }
+ } else {
+ quote! {}
+ }
+ }
+ Self::Named(ref fields) => {
+ let fields = fields.iter().map(|(name, ty)| {
+ let compact_attr = ty.compact_attr();
+ quote! { #compact_attr #visibility #name: #ty }
+ });
+ let marker = phantom_data.map(|phantom_data| {
+ quote!(
+ #[codec(skip)]
+ #visibility __subxt_unused_type_params: #phantom_data
+ )
+ });
+ quote!(
+ {
+ #( #fields, )*
+ #marker
+ }
+ )
+ }
+ Self::Unnamed(ref fields) => {
+ let fields = fields.iter().map(|ty| {
+ let compact_attr = ty.compact_attr();
+ quote! { #compact_attr #visibility #ty }
+ });
+ let marker = phantom_data.map(|phantom_data| {
+ quote!(
+ #[codec(skip)]
+ #visibility #phantom_data
+ )
+ });
+ quote! {
+ (
+ #( #fields, )*
+ #marker
+ )
+ }
+ }
+ }
+ }
+
+ /// Generate the code for fields which will compose an `enum` variant.
+ pub fn to_enum_variant_field_tokens(&self) -> TokenStream {
+ match self {
+ Self::NoFields => quote! {},
+ Self::Named(ref fields) => {
+ let fields = fields.iter().map(|(name, ty)| {
+ let compact_attr = ty.compact_attr();
+ quote! { #compact_attr #name: #ty }
+ });
+ quote!( { #( #fields, )* } )
+ }
+ Self::Unnamed(ref fields) => {
+ let fields = fields.iter().map(|ty| {
+ let compact_attr = ty.compact_attr();
+ quote! { #compact_attr #ty }
+ });
+ quote! { ( #( #fields, )* ) }
+ }
+ }
+ }
+}
+
+/// Represents a field of a composite type to be generated.
+#[derive(Debug)]
+pub struct CompositeDefFieldType {
+ pub type_id: u32,
+ pub type_path: TypePath,
+ pub type_name: Option,
+}
+
+impl CompositeDefFieldType {
+ /// Construct a new [`CompositeDefFieldType`].
+ pub fn new(type_id: u32, type_path: TypePath, type_name: Option) -> Self {
+ CompositeDefFieldType {
+ type_id,
+ type_path,
+ type_name,
+ }
+ }
+
+ /// Returns `true` if the field is a [`::std::boxed::Box`].
+ pub fn is_boxed(&self) -> bool {
+ // Use the type name to detect a `Box` field.
+ // Should be updated once `Box` types are no longer erased:
+ // https://github.com/paritytech/scale-info/pull/82
+ matches!(&self.type_name, Some(ty_name) if ty_name.contains("Box<"))
+ }
+
+ /// Returns the `#[codec(compact)]` attribute if the type is compact.
+ fn compact_attr(&self) -> Option {
+ self.type_path
+ .is_compact()
+ .then(|| quote!( #[codec(compact)] ))
+ }
+}
+
+impl quote::ToTokens for CompositeDefFieldType {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let ty_path = &self.type_path;
+
+ if self.is_boxed() {
+ tokens.extend(quote! { ::std::boxed::Box<#ty_path> })
+ } else {
+ tokens.extend(quote! { #ty_path })
+ };
+ }
+}
diff --git a/codegen/src/derives.rs b/codegen/src/types/derives.rs
similarity index 75%
rename from codegen/src/derives.rs
rename to codegen/src/types/derives.rs
index cd7c7b8850..db09aade91 100644
--- a/codegen/src/derives.rs
+++ b/codegen/src/types/derives.rs
@@ -14,7 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with subxt. If not, see .
-use syn::punctuated::Punctuated;
+use syn::{
+ parse_quote,
+ punctuated::Punctuated,
+};
#[derive(Debug, Clone)]
pub struct GeneratedTypeDerives {
@@ -26,11 +29,20 @@ impl GeneratedTypeDerives {
Self { derives }
}
+ /// Add `::subxt::codec::CompactAs` to the derives.
+ pub fn push_codec_compact_as(&mut self) {
+ self.derives.push(parse_quote!(::subxt::codec::CompactAs));
+ }
+
pub fn append(&mut self, derives: impl Iterator- ) {
for derive in derives {
self.derives.push(derive)
}
}
+
+ pub fn push(&mut self, derive: syn::Path) {
+ self.derives.push(derive);
+ }
}
impl Default for GeneratedTypeDerives {
@@ -45,9 +57,11 @@ impl Default for GeneratedTypeDerives {
impl quote::ToTokens for GeneratedTypeDerives {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
- let derives = &self.derives;
- tokens.extend(quote::quote! {
- #[derive(#derives)]
- })
+ if !self.derives.is_empty() {
+ let derives = &self.derives;
+ tokens.extend(quote::quote! {
+ #[derive(#derives)]
+ })
+ }
}
}
diff --git a/codegen/src/types/mod.rs b/codegen/src/types/mod.rs
index a5f3502af3..b6972bdc7f 100644
--- a/codegen/src/types/mod.rs
+++ b/codegen/src/types/mod.rs
@@ -14,12 +14,14 @@
// You should have received a copy of the GNU General Public License
// along with subxt. If not, see .
+mod composite_def;
+mod derives;
#[cfg(test)]
mod tests;
mod type_def;
+mod type_def_params;
mod type_path;
-use super::GeneratedTypeDerives;
use proc_macro2::{
Ident,
Span,
@@ -41,7 +43,14 @@ use std::collections::{
};
pub use self::{
+ composite_def::{
+ CompositeDef,
+ CompositeDefFieldType,
+ CompositeDefFields,
+ },
+ derives::GeneratedTypeDerives,
type_def::TypeDefGen,
+ type_def_params::TypeDefParameters,
type_path::{
TypeParameter,
TypePath,
@@ -50,6 +59,8 @@ pub use self::{
},
};
+pub type Field = scale_info::Field;
+
/// Generate a Rust module containing all types defined in the supplied [`PortableRegistry`].
#[derive(Debug)]
pub struct TypeGenerator<'a> {
@@ -126,7 +137,7 @@ impl<'a> TypeGenerator<'a> {
if path.len() == 1 {
child_mod
.types
- .insert(ty.path().clone(), TypeDefGen { ty, type_gen: self });
+ .insert(ty.path().clone(), TypeDefGen::from_type(ty, self));
} else {
self.insert_type(ty, id, path[1..].to_vec(), root_mod_ident, child_mod)
}
diff --git a/codegen/src/types/tests.rs b/codegen/src/types/tests.rs
index 16cce59a49..a1f2fe094a 100644
--- a/codegen/src/types/tests.rs
+++ b/codegen/src/types/tests.rs
@@ -237,44 +237,34 @@ fn derive_compact_as_for_uint_wrapper_structs() {
pub mod tests {
use super::root;
- #[derive(::subxt::codec::CompactAs)]
- #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
+ #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug, ::subxt::codec::CompactAs)]
pub struct Su128 { pub a: ::core::primitive::u128, }
- #[derive(::subxt::codec::CompactAs)]
- #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
+ #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug, ::subxt::codec::CompactAs)]
pub struct Su16 { pub a: ::core::primitive::u16, }
- #[derive(::subxt::codec::CompactAs)]
- #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
+ #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug, ::subxt::codec::CompactAs)]
pub struct Su32 { pub a: ::core::primitive::u32, }
- #[derive(::subxt::codec::CompactAs)]
- #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
+ #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug, ::subxt::codec::CompactAs)]
pub struct Su64 { pub a: ::core::primitive::u64, }
- #[derive(::subxt::codec::CompactAs)]
- #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
+ #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug, ::subxt::codec::CompactAs)]
pub struct Su8 { pub a: ::core::primitive::u8, }
- #[derive(::subxt::codec::CompactAs)]
- #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
+ #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug, ::subxt::codec::CompactAs)]
pub struct TSu128(pub ::core::primitive::u128,);
- #[derive(::subxt::codec::CompactAs)]
- #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
+ #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug, ::subxt::codec::CompactAs)]
pub struct TSu16(pub ::core::primitive::u16,);
- #[derive(::subxt::codec::CompactAs)]
- #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
+ #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug, ::subxt::codec::CompactAs)]
pub struct TSu32(pub ::core::primitive::u32,);
- #[derive(::subxt::codec::CompactAs)]
- #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
+ #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug, ::subxt::codec::CompactAs)]
pub struct TSu64(pub ::core::primitive::u64,);
- #[derive(::subxt::codec::CompactAs)]
- #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
+ #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug, ::subxt::codec::CompactAs)]
pub struct TSu8(pub ::core::primitive::u8,);
}
}
@@ -771,16 +761,15 @@ fn generics_with_alias_adds_phantom_data_marker() {
quote! {
pub mod tests {
use super::root;
- #[derive(::subxt::codec::CompactAs)]
- #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
+ #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug, ::subxt::codec::CompactAs)]
pub struct NamedFields<_0> {
pub b: ::core::primitive::u32,
- #[codec(skip)] pub __subxt_unused_type_params: ::core::marker::PhantomData<_0>,
+ #[codec(skip)] pub __subxt_unused_type_params: ::core::marker::PhantomData<_0>
}
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub struct UnnamedFields<_0, _1> (
pub (::core::primitive::u32, ::core::primitive::u32,),
- #[codec(skip)] pub ::core::marker::PhantomData<(_0, _1)>,
+ #[codec(skip)] pub ::core::marker::PhantomData<(_0, _1)>
);
}
}
@@ -794,7 +783,7 @@ fn modules() {
pub mod a {
#[allow(unused)]
#[derive(scale_info::TypeInfo)]
- pub struct Foo {}
+ pub struct Foo;
pub mod b {
#[allow(unused)]
@@ -847,7 +836,7 @@ fn modules() {
}
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
- pub struct Foo {}
+ pub struct Foo;
}
pub mod c {
diff --git a/codegen/src/types/type_def.rs b/codegen/src/types/type_def.rs
index f0552f167b..87b9f6c96f 100644
--- a/codegen/src/types/type_def.rs
+++ b/codegen/src/types/type_def.rs
@@ -15,9 +15,12 @@
// along with subxt. If not, see .
use super::{
+ CompositeDef,
+ CompositeDefFields,
+ GeneratedTypeDerives,
+ TypeDefParameters,
TypeGenerator,
TypeParameter,
- TypePath,
};
use proc_macro2::TokenStream;
use quote::{
@@ -26,12 +29,9 @@ use quote::{
};
use scale_info::{
form::PortableForm,
- Field,
Type,
TypeDef,
- TypeDefPrimitive,
};
-use std::collections::HashSet;
use syn::parse_quote;
/// Generates a Rust `struct` or `enum` definition based on the supplied [`scale-info::Type`].
@@ -40,17 +40,20 @@ use syn::parse_quote;
/// generated types in the module.
#[derive(Debug)]
pub struct TypeDefGen<'a> {
- /// The type generation context, allows resolving of type paths for the fields of the
- /// generated type.
- pub(super) type_gen: &'a TypeGenerator<'a>,
- /// Contains the definition of the type to be generated.
- pub(super) ty: Type,
+ /// The type parameters of the type to be generated
+ type_params: TypeDefParameters,
+ /// The derives with which to annotate the generated type.
+ derives: &'a GeneratedTypeDerives,
+ /// The kind of type to be generated.
+ ty_kind: TypeDefGenKind,
}
-impl<'a> quote::ToTokens for TypeDefGen<'a> {
- fn to_tokens(&self, tokens: &mut TokenStream) {
- let type_params = self
- .ty
+impl<'a> TypeDefGen<'a> {
+ /// Construct a type definition for codegen from the given [`scale_info::Type`].
+ pub fn from_type(ty: Type, type_gen: &'a TypeGenerator) -> Self {
+ let derives = type_gen.derives();
+
+ let type_params = ty
.type_params()
.iter()
.enumerate()
@@ -60,6 +63,7 @@ impl<'a> quote::ToTokens for TypeDefGen<'a> {
let tp_name = format_ident!("_{}", i);
Some(TypeParameter {
concrete_type_id: ty.id(),
+ original_name: tp.name().clone(),
name: tp_name,
})
}
@@ -68,267 +72,100 @@ impl<'a> quote::ToTokens for TypeDefGen<'a> {
})
.collect::>();
- let type_name = self.ty.path().ident().map(|ident| {
- let type_params = if !type_params.is_empty() {
- quote! { < #( #type_params ),* > }
- } else {
- quote! {}
- };
- let ty = format_ident!("{}", ident);
- let path = parse_quote! { #ty #type_params};
- syn::Type::Path(path)
- });
+ let mut type_params = TypeDefParameters::new(type_params);
- let derives = self.type_gen.derives();
-
- match self.ty.type_def() {
+ let ty_kind = match ty.type_def() {
TypeDef::Composite(composite) => {
- let type_name = type_name.expect("structs should have a name");
- let (fields, _) =
- self.composite_fields(composite.fields(), &type_params, true);
- let derive_as_compact = if composite.fields().len() == 1 {
- // any single field wrapper struct with a concrete unsigned int type can derive
- // CompactAs.
- let field = &composite.fields()[0];
- if !self
- .ty
- .type_params()
- .iter()
- .any(|tp| Some(tp.name()) == field.type_name())
- {
- let ty = self.type_gen.resolve_type(field.ty().id());
- if matches!(
- ty.type_def(),
- TypeDef::Primitive(
- TypeDefPrimitive::U8
- | TypeDefPrimitive::U16
- | TypeDefPrimitive::U32
- | TypeDefPrimitive::U64
- | TypeDefPrimitive::U128
- )
- ) {
- Some(quote!( #[derive(::subxt::codec::CompactAs)] ))
- } else {
- None
- }
- } else {
- None
- }
- } else {
- None
- };
-
- let ty_toks = quote! {
- #derive_as_compact
- #derives
- pub struct #type_name #fields
- };
- tokens.extend(ty_toks);
+ let type_name = ty.path().ident().expect("structs should have a name");
+ let fields = CompositeDefFields::from_scale_info_fields(
+ &type_name,
+ composite.fields(),
+ type_params.params(),
+ type_gen,
+ );
+ type_params.update_unused(fields.field_types());
+ let composite_def = CompositeDef::struct_def(
+ &type_name,
+ type_params.clone(),
+ fields,
+ Some(parse_quote!(pub)),
+ type_gen,
+ );
+ TypeDefGenKind::Struct(composite_def)
}
TypeDef::Variant(variant) => {
- let type_name = type_name.expect("variants should have a name");
- let mut variants = Vec::new();
- let mut used_type_params = HashSet::new();
- let type_params_set: HashSet<_> = type_params.iter().cloned().collect();
+ let type_name = ty.path().ident().expect("variants should have a name");
+ let variants = variant
+ .variants()
+ .iter()
+ .map(|v| {
+ let fields = CompositeDefFields::from_scale_info_fields(
+ v.name(),
+ v.fields(),
+ type_params.params(),
+ type_gen,
+ );
+ type_params.update_unused(fields.field_types());
+ let variant_def =
+ CompositeDef::enum_variant_def(v.name(), fields);
+ (v.index(), variant_def)
+ })
+ .collect();
- for v in variant.variants() {
- let variant_name = format_ident!("{}", v.name());
- let (fields, unused_type_params) = if v.fields().is_empty() {
- let unused = type_params_set.iter().cloned().collect::>();
- (quote! {}, unused)
- } else {
- self.composite_fields(v.fields(), &type_params, false)
- };
- let index = proc_macro2::Literal::u8_unsuffixed(v.index());
- variants.push(quote! {
- #[codec(index = #index)]
- #variant_name #fields
- });
- let unused_params_set = unused_type_params.iter().cloned().collect();
- let used_params = type_params_set.difference(&unused_params_set);
+ TypeDefGenKind::Enum(type_name, variants)
+ }
+ _ => TypeDefGenKind::BuiltIn,
+ };
- for used_param in used_params {
- used_type_params.insert(used_param.clone());
- }
- }
+ Self {
+ type_params,
+ derives,
+ ty_kind,
+ }
+ }
+}
- let unused_type_params = type_params_set
- .difference(&used_type_params)
- .cloned()
+impl<'a> quote::ToTokens for TypeDefGen<'a> {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ match &self.ty_kind {
+ TypeDefGenKind::Struct(composite) => composite.to_tokens(tokens),
+ TypeDefGenKind::Enum(type_name, variants) => {
+ let mut variants = variants
+ .iter()
+ .map(|(index, def)| {
+ let index = proc_macro2::Literal::u8_unsuffixed(*index);
+ quote! {
+ #[codec(index = #index)]
+ #def
+ }
+ })
.collect::>();
- if !unused_type_params.is_empty() {
- let phantom = Self::phantom_data(&unused_type_params);
+
+ if let Some(phantom) = self.type_params.unused_params_phantom_data() {
variants.push(quote! {
__Ignore(#phantom)
})
}
+ let enum_ident = format_ident!("{}", type_name);
+ let type_params = &self.type_params;
+ let derives = self.derives;
let ty_toks = quote! {
#derives
- pub enum #type_name {
+ pub enum #enum_ident #type_params {
#( #variants, )*
}
};
tokens.extend(ty_toks);
}
- _ => (), // all built-in types should already be in scope
+ TypeDefGenKind::BuiltIn => (), /* all built-in types should already be in scope */
}
}
}
-impl<'a> TypeDefGen<'a> {
- fn composite_fields(
- &self,
- fields: &'a [Field],
- type_params: &'a [TypeParameter],
- is_struct: bool,
- ) -> (TokenStream, Vec) {
- let named = fields.iter().all(|f| f.name().is_some());
- let unnamed = fields.iter().all(|f| f.name().is_none());
-
- fn unused_type_params<'a>(
- type_params: &'a [TypeParameter],
- types: impl Iterator
- ,
- ) -> Vec {
- let mut used_type_params = HashSet::new();
- for ty in types {
- ty.parent_type_params(&mut used_type_params)
- }
- let type_params_set: HashSet<_> = type_params.iter().cloned().collect();
- let mut unused = type_params_set
- .difference(&used_type_params)
- .cloned()
- .collect::>();
- unused.sort();
- unused
- }
-
- let ty_toks = |ty_name: &str, ty_path: &TypePath| {
- if ty_name.contains("Box<") {
- quote! { ::std::boxed::Box<#ty_path> }
- } else {
- quote! { #ty_path }
- }
- };
-
- if named {
- let fields = fields
- .iter()
- .map(|field| {
- let name = format_ident!(
- "{}",
- field.name().expect("named field without a name")
- );
- let ty = self
- .type_gen
- .resolve_type_path(field.ty().id(), type_params);
- (name, ty, field.type_name())
- })
- .collect::>();
-
- let mut fields_tokens = fields
- .iter()
- .map(|(name, ty, ty_name)| {
- let field_type = match ty_name {
- Some(ty_name) => {
- let ty = ty_toks(ty_name, ty);
- if is_struct {
- quote! ( pub #name: #ty )
- } else {
- quote! ( #name: #ty )
- }
- }
- None => {
- quote! ( #name: #ty )
- }
- };
- if ty.is_compact() {
- quote!( #[codec(compact)] #field_type )
- } else {
- quote!( #field_type )
- }
- })
- .collect::>();
-
- let unused_params =
- unused_type_params(type_params, fields.iter().map(|(_, ty, _)| ty));
-
- if is_struct && !unused_params.is_empty() {
- let phantom = Self::phantom_data(&unused_params);
- fields_tokens.push(quote! {
- #[codec(skip)] pub __subxt_unused_type_params: #phantom
- })
- }
-
- let fields = quote! {
- {
- #( #fields_tokens, )*
- }
- };
- (fields, unused_params)
- } else if unnamed {
- let type_paths = fields
- .iter()
- .map(|field| {
- let ty = self
- .type_gen
- .resolve_type_path(field.ty().id(), type_params);
- (ty, field.type_name())
- })
- .collect::>();
- let mut fields_tokens = type_paths
- .iter()
- .map(|(ty, ty_name)| {
- let field_type = match ty_name {
- Some(ty_name) => {
- let ty = ty_toks(ty_name, ty);
- if is_struct {
- quote! { pub #ty }
- } else {
- quote! { #ty }
- }
- }
- None => {
- quote! { #ty }
- }
- };
- if ty.is_compact() {
- quote!( #[codec(compact)] #field_type )
- } else {
- quote!( #field_type )
- }
- })
- .collect::>();
-
- let unused_params =
- unused_type_params(type_params, type_paths.iter().map(|(ty, _)| ty));
-
- if is_struct && !unused_params.is_empty() {
- let phantom_data = Self::phantom_data(&unused_params);
- fields_tokens.push(quote! { #[codec(skip)] pub #phantom_data })
- }
-
- let fields = quote! { ( #( #fields_tokens, )* ) };
- let fields_tokens = if is_struct {
- // add a semicolon for tuple structs
- quote! { #fields; }
- } else {
- fields
- };
-
- (fields_tokens, unused_params)
- } else {
- panic!("Fields must be either all named or all unnamed")
- }
- }
-
- fn phantom_data(params: &[TypeParameter]) -> TokenStream {
- let params = if params.len() == 1 {
- let param = ¶ms[0];
- quote! { #param }
- } else {
- quote! { ( #( #params ), * ) }
- };
- quote! ( ::core::marker::PhantomData<#params> )
- }
+#[derive(Debug)]
+pub enum TypeDefGenKind {
+ Struct(CompositeDef),
+ Enum(String, Vec<(u8, CompositeDef)>),
+ BuiltIn,
}
diff --git a/codegen/src/types/type_def_params.rs b/codegen/src/types/type_def_params.rs
new file mode 100644
index 0000000000..c5508865b0
--- /dev/null
+++ b/codegen/src/types/type_def_params.rs
@@ -0,0 +1,87 @@
+// Copyright 2019-2022 Parity Technologies (UK) Ltd.
+// This file is part of subxt.
+//
+// subxt is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// subxt is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with subxt. If not, see .
+
+use super::TypeParameter;
+use crate::types::CompositeDefFieldType;
+use quote::quote;
+use std::collections::BTreeSet;
+
+/// Represents the set of generic type parameters for generating a type definition e.g. the `T` in
+/// `Foo`.
+///
+/// Additionally this allows generating a `PhantomData` type for any type params which are unused
+/// in the type definition itself.
+#[derive(Clone, Debug, Default)]
+pub struct TypeDefParameters {
+ params: Vec,
+ unused: BTreeSet,
+}
+
+impl TypeDefParameters {
+ /// Create a new [`TypeDefParameters`] instance.
+ pub fn new(params: Vec) -> Self {
+ let unused = params.iter().cloned().collect();
+ Self { params, unused }
+ }
+
+ /// Update the set of unused type parameters by removing those that are used in the given
+ /// fields.
+ pub fn update_unused<'a>(
+ &mut self,
+ fields: impl Iterator
- ,
+ ) {
+ let mut used_type_params = BTreeSet::new();
+ for field in fields {
+ field.type_path.parent_type_params(&mut used_type_params)
+ }
+ for used_type_param in &used_type_params {
+ self.unused.remove(used_type_param);
+ }
+ }
+
+ /// Construct a [`core::marker::PhantomData`] for the type unused type params.
+ pub fn unused_params_phantom_data(&self) -> Option {
+ if self.unused.is_empty() {
+ return None
+ }
+ let params = if self.unused.len() == 1 {
+ let param = self
+ .unused
+ .iter()
+ .next()
+ .expect("Checked for exactly one unused param");
+ quote! { #param }
+ } else {
+ let params = self.unused.iter();
+ quote! { ( #( #params ), * ) }
+ };
+ Some(syn::parse_quote! { ::core::marker::PhantomData<#params> })
+ }
+
+ /// Returns the set of type parameters.
+ pub fn params(&self) -> &[TypeParameter] {
+ &self.params
+ }
+}
+
+impl<'a> quote::ToTokens for TypeDefParameters {
+ fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
+ if !self.params.is_empty() {
+ let params = &self.params;
+ tokens.extend(quote! { < #( #params ),* > })
+ }
+ }
+}
diff --git a/codegen/src/types/type_path.rs b/codegen/src/types/type_path.rs
index 6ebef9e779..b0c239954c 100644
--- a/codegen/src/types/type_path.rs
+++ b/codegen/src/types/type_path.rs
@@ -28,7 +28,7 @@ use scale_info::{
TypeDef,
TypeDefPrimitive,
};
-use std::collections::HashSet;
+use std::collections::BTreeSet;
use syn::parse_quote;
#[derive(Clone, Debug)]
@@ -67,7 +67,7 @@ impl TypePath {
/// a: Vec