mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-23 06:08:01 +00:00
Refactor type generation, remove code duplication (#352)
* codegen: fix compact unnamed fields * Fmt * codegen: move derives and struct_def to types * codegen: rename struct_def to composite_def.r * WIP: deduplicate struct def code * Fmt * WIP refactoring composite type codegen duplication * Fmt * Fix TokenStream import * Fix field_tokens ty_path parse error * Fix call struct generation * Refactor ty_path() * Optional derives and move CompactAs derive to composite_def * Fmt * Introduce CompositeDefFieldType * Restore default codec derives * Extract TypeDefParameters and TypeDefGen construction * Fmt * Reset codegen to master * Introduce CompositeDefFields * Introduce CompositeDefFields * Fix up errors * Fix Box field types * Fmt * Fix compact attribute * Handle no fields case * Handle no fields with trailing semi * Fix compact field detection * Fix generic phantom marker * Fmt * Fix generic type parm fields * Clippy * Fix up boxed call fields * Fmt * Add comments to composite_def.rs * Fix license headers * Restore Debug derive in tests * Fix empty struct codegen test * Use BTreeSet for type params for ordering * code review: fix comment * code review: refactor CompositeDefFields as enum * Fix empty named fields * Fix generation of call variant enum structs * Expand field_tokens into separate methods for struct and enum variants * Add TypeDefParameters docs * Fix doc link * Clippy redundant return
This commit is contained in:
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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<syn::Visibility>,
|
||||
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<syn::Visibility>,
|
||||
},
|
||||
/// 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<CompositeDefFieldType>),
|
||||
}
|
||||
|
||||
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<dyn Iterator<Item = &CompositeDefFieldType> + '_> {
|
||||
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<syn::TypePath>,
|
||||
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<String>,
|
||||
}
|
||||
|
||||
impl CompositeDefFieldType {
|
||||
/// Construct a new [`CompositeDefFieldType`].
|
||||
pub fn new(type_id: u32, type_path: TypePath, type_name: Option<String>) -> 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<TokenStream> {
|
||||
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 })
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
use syn::{
|
||||
parse_quote,
|
||||
punctuated::Punctuated,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GeneratedTypeDerives {
|
||||
derives: Punctuated<syn::Path, syn::Token![,]>,
|
||||
}
|
||||
|
||||
impl GeneratedTypeDerives {
|
||||
pub fn new(derives: Punctuated<syn::Path, syn::Token!(,)>) -> Self {
|
||||
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<Item = syn::Path>) {
|
||||
for derive in derives {
|
||||
self.derives.push(derive)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push(&mut self, derive: syn::Path) {
|
||||
self.derives.push(derive);
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for GeneratedTypeDerives {
|
||||
fn default() -> Self {
|
||||
let mut derives = Punctuated::new();
|
||||
derives.push(syn::parse_quote!(::subxt::codec::Encode));
|
||||
derives.push(syn::parse_quote!(::subxt::codec::Decode));
|
||||
derives.push(syn::parse_quote!(Debug));
|
||||
Self::new(derives)
|
||||
}
|
||||
}
|
||||
|
||||
impl quote::ToTokens for GeneratedTypeDerives {
|
||||
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||
if !self.derives.is_empty() {
|
||||
let derives = &self.derives;
|
||||
tokens.extend(quote::quote! {
|
||||
#[derive(#derives)]
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,12 +14,14 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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<PortableForm>;
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
+15
-26
@@ -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 {
|
||||
|
||||
+89
-252
@@ -15,9 +15,12 @@
|
||||
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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<PortableForm>,
|
||||
/// 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<PortableForm>, 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::<Vec<_>>();
|
||||
|
||||
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::<Vec<_>>();
|
||||
(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::<Vec<_>>();
|
||||
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<PortableForm>],
|
||||
type_params: &'a [TypeParameter],
|
||||
is_struct: bool,
|
||||
) -> (TokenStream, Vec<TypeParameter>) {
|
||||
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<Item = &'a TypePath>,
|
||||
) -> Vec<TypeParameter> {
|
||||
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::<Vec<_>>();
|
||||
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::<Vec<_>>();
|
||||
|
||||
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::<Vec<_>>();
|
||||
|
||||
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::<Vec<_>>();
|
||||
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::<Vec<_>>();
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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<T>`.
|
||||
///
|
||||
/// 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<TypeParameter>,
|
||||
unused: BTreeSet<TypeParameter>,
|
||||
}
|
||||
|
||||
impl TypeDefParameters {
|
||||
/// Create a new [`TypeDefParameters`] instance.
|
||||
pub fn new(params: Vec<TypeParameter>) -> 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<Item = &'a CompositeDefFieldType>,
|
||||
) {
|
||||
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<syn::TypePath> {
|
||||
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 ),* > })
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<Option<T>>, // the parent type param here is `T`
|
||||
/// }
|
||||
/// ```
|
||||
pub fn parent_type_params(&self, acc: &mut HashSet<TypeParameter>) {
|
||||
pub fn parent_type_params(&self, acc: &mut BTreeSet<TypeParameter>) {
|
||||
match self {
|
||||
Self::Parameter(type_parameter) => {
|
||||
acc.insert(type_parameter.clone());
|
||||
@@ -173,7 +173,7 @@ impl TypePathType {
|
||||
}
|
||||
TypeDef::Compact(_) => {
|
||||
let compact_type = &self.params[0];
|
||||
syn::Type::Path(parse_quote! ( #compact_type ))
|
||||
parse_quote! ( #compact_type )
|
||||
}
|
||||
TypeDef::BitSequence(_) => {
|
||||
let bit_order_type = &self.params[0];
|
||||
@@ -195,7 +195,7 @@ impl TypePathType {
|
||||
/// a: Vec<Option<T>>, // the parent type param here is `T`
|
||||
/// }
|
||||
/// ```
|
||||
fn parent_type_params(&self, acc: &mut HashSet<TypeParameter>) {
|
||||
fn parent_type_params(&self, acc: &mut BTreeSet<TypeParameter>) {
|
||||
for p in &self.params {
|
||||
p.parent_type_params(acc);
|
||||
}
|
||||
@@ -205,6 +205,7 @@ impl TypePathType {
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct TypeParameter {
|
||||
pub(super) concrete_type_id: u32,
|
||||
pub(super) original_name: String,
|
||||
pub(super) name: proc_macro2::Ident,
|
||||
}
|
||||
|
||||
@@ -235,7 +236,7 @@ impl quote::ToTokens for TypePathSubstitute {
|
||||
}
|
||||
|
||||
impl TypePathSubstitute {
|
||||
fn parent_type_params(&self, acc: &mut HashSet<TypeParameter>) {
|
||||
fn parent_type_params(&self, acc: &mut BTreeSet<TypeParameter>) {
|
||||
for p in &self.params {
|
||||
p.parent_type_params(acc);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user