Generate runtime API from metadata (#294)

* Remove test macro

* Remove client crate

* Create tests crate and move pallet specific tests there

* Extract client, remove metadata and extra, more demolition

* Update substrate dependencies to git dependencies

* Remove Store stuff for now

* Comment out some Call usages

* Add back Runtime trait coped from original System trait

* Make subxt lib compile

* Delete old proc macros and copy over type generation from chameleon

* WIP make transfer balance test pass

* Change to subxt attribute macro

* WIP provide user defined type substitutes

* User defined type substitutes compile

* WIP submitting transactions

* WIP transfer balance test

* Fix macro

* Cargo fmt

* WIP generating storage hashers

* WIP add AccountData trait for fetching the nonce

* Support single type storage map keys

* WIP impl AccountInfo retrieval

* Fix up storage struct generation

* Implement AccountData triait directly on storage entry

* Borrow storage map key and convert account id

* Implement storage fetch client methods

* Remove legacy metadata storage key construction

* Rename CheckEra to CheckMortality

* Substitute perthings types for compact impls

* Fmt

* Downgrade dyn-clone for cargo-contract compat

* Scale-fo 1.0

* scale-info 1.0

* Remove special range handling

* Restore wildcard type params

* Frame metadata 14.0

* WIP decoding events

* WIP more dynamically decoding events

* Fmt

* Decode events, handle errors

* Uncomment some tests

* Remove unused get_mod function

* Fix some warnings

* Fix some more warnings

* Fix some more warnings

* Add tests mod

* Rename node-runtime tests mod to frame

* Fix some warnings

* Fmt

* WIP generate storage client with getters

* Storage client compiling

* Generate storage client api

* Fix up system account query account ids

* WIP generating tx api fns

* Only generate tx api fields when calls available

* Fix tx api call fns

* Fmt

* WIP generate event structs

* call functions not async

* Derive Eq for comparison on generated types

* Generate event structs

* Fix call name

* Fmt

* Update node runtime metadata to substrate c000780db

* Download latest substrate release for integration testing

* Fix event decoding

* Remove unused imports

* Fix plain storage access, total_issuance pass

* Fmt

* Restore contracts tests

* Backoff connecting to substrate node

* Add required TypeInfo impls for local SignedExtension impls

* Remove unnecessary assert formatting

* Fix handling of DispatchError

* Refactor contracts tests

* Troubleshooting contract not found

* Remove more client feature stuff

* Fix dynamic event variant decoding, write consumed index to output

* Fmt

* Use substrate branch with heavy dependency removed

* Remove sp-rcp dependency, define types locally

* Ignore cargo timeing files

* Use my branch for substrate test deps

* Fix storage key type gen

* Comment out fetching contract info

* Add key iteration, extract storage client from main client

* Debugging key generation

* Use substrate master branch

* Fix call test

* Remove TypeSegmenter and dynclone dependency

* Publicly expose Rpc mod

* Unused import warnings

* Add getter for runtime metadata

* Add pallet and event indices for raw events

* Add is_call and is_event convenience trait functions

* Add missing docs

* Refactor tests crate

* Restore remaining client tests

* Fmt

* Fix warnings

* Restore get_mod as test helper and fmt

* Use client references for api calls

* Fix api usages with methods

* Use Bytes for RawEvent debug

* Update metadata

* Restoring some Balances tests

* Populate runtime storage metadata

* Restore balances lock test

* Restore Balances error test

* Fmt

* Restore transfer subscription API

* Staking test

* Restore another staking test

* Restore another staking test

* Restore another staking test

* Partially restore chill_works_for_controller_only staking test

* Fix fetching Optional storage entries

* Restore staking bond test

* Restore remaining staking tests

* Fmt

* Restore sudo tests

* Add some system tests

* Fmt

* Resolve some todos

* Remove pass through rpc methods on Client, expose via rpc() getter

* Remove more rpc pass through methods

* Remove submit tx pass through rpc methods

* Add some comments to SubmittableExtrinsic methods

* Construct the runtime api from the client

* Fmt

* Use From trait instead of new for AccountData query

* Rename subxt_proc_macro crate to subxt_macro

* Fix AccountData From impl

* Extract codegen crate from macro crate

* Fmt

* Replace chameleon hidden field name

* Extract StructDef for generating structs

* More refactoring of StructDef, moving towards sharing with typegen

* Replace explicit tests crate with single implicit integration tests crate

* Rename from substrate-subxt to subxt

* Fix runtime path relative to root Cargo.toml

* Move RpcClient creation to RpcClient

* WIP get examples to compile

* Rename Runtime to Config trait

* WIP implementing default Config

* WIP implementing default extrinsic extras

* fix metadata constants (#299)

* Move DefaultConfig definition and impl to macro

* Extract type substitute parsing to ir mod

* Extract calls, events and storage from api generation

* Add some hardcoded type substitute defaults

* Fmt

* Add utility pallet tests (#300)

* add batch call test example

* add pallet utility tests

* add utility module

* fix warnings

* Add polkadot runtime metadata for example

* Fix system errors and fmt

* Add subxt-cli crate

* Add metadata and codegen subcommands

* Make subxt-cli codegen command work

* Fmt

* Add polkadot codegen test

* Comment about how to run codegen

* Derive AsCompact for structs with single concrete unsigned int field

* Fix bitvec codegen, adds as non optional dependency

* Regenerate polkadot api with bitvec fix

* Edition 2021

* Fix polkadot codegen with bitvec

* Polkadot balance transfer is working

* Fix fetch remote

* Fix transfer_subscribe example

* Fix submit_and_watch example

* Fmt

* Generate storage iter method for iterating over keys

* Fmt

* Fix existential deposit test

* Fix staking tests

* Add option for custom generated type derives

* Add generated type derives for test runtime api

* Fmt

* Copy WrapperTypeOpaque from substrate, add Encode/Decode

* Fmt

* Extract type generator to module, separate & fix tests

* Fully qualified primitive and prelude types

* Fix up remaining type gen tests

* Skip formatting of generated polkadot example code

* Remove empty utility test file.

* Newline

* Update cli/src/main.rs

Co-authored-by: David <dvdplm@gmail.com>

* Rename subxt-cli executable to subxt

* Update src/client.rs

Co-authored-by: David <dvdplm@gmail.com>

* Add some code docs to TypeGenerator.

* Extract TypePath to own file

* Extract type def generation to separate file

* Renamed ModuleType to TypeDefGen

* Fmt

* Factor out type parameter from final_key

* Fix some type paths

* Resolve some todos

* Resolve some panic todos in events

* Add EventsDecodingError

* Decode compact composite types with a single primitive field

* Decode compact composite types with a single primitive field

* Update src/metadata.rs

Co-authored-by: Andrew Plaza <aplaza@liquidthink.net>

* Remove Perbill compact substitute types

* Remove todos regarding maintaining Rust code items, promoted to follow up issue.

* Remove todo regarding overridding default config impl

* Remove todo regarding overridding default Extra

* Remove todo regarding AccountData storage type defintion

* Remove todo regarding borrowing storage key arguments

* Remove type substitution tests todo

* Remove `Box` field name type hack todo

* Remove Compact todo

* Remove sudo todos

* Remove BitVec implementation todo

* Fmt

* Add health warning to README

* Fix up health warning

Co-authored-by: Paulo Martins <paulormart@users.noreply.github.com>
Co-authored-by: David <dvdplm@gmail.com>
Co-authored-by: Andrew Plaza <aplaza@liquidthink.net>
This commit is contained in:
Andrew Jones
2021-11-03 11:28:59 +00:00
committed by GitHub
parent 8f0641e660
commit 793c945fbd
80 changed files with 5929 additions and 6475 deletions
+253
View File
@@ -0,0 +1,253 @@
// Copyright 2019-2021 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/>.
#[cfg(test)]
mod tests;
mod type_def;
mod type_path;
use super::GeneratedTypeDerives;
use proc_macro2::{
Ident,
Span,
TokenStream,
};
use quote::{
quote,
ToTokens,
};
use scale_info::{
form::PortableForm,
PortableRegistry,
Type,
TypeDef,
};
use std::collections::{
BTreeMap,
HashMap,
};
pub use self::{
type_def::TypeDefGen,
type_path::{
TypeParameter,
TypePath,
TypePathSubstitute,
TypePathType,
},
};
/// Generate a Rust module containing all types defined in the supplied [`PortableRegistry`].
#[derive(Debug)]
pub struct TypeGenerator<'a> {
/// The name of the module which will contain the generated types.
types_mod_ident: Ident,
/// Registry of type definitions to be transformed into Rust type definitions.
type_registry: &'a PortableRegistry,
/// User defined overrides for generated types.
type_substitutes: HashMap<String, syn::TypePath>,
/// Set of derives with which to annotate generated types.
derives: GeneratedTypeDerives,
}
impl<'a> TypeGenerator<'a> {
/// Construct a new [`TypeGenerator`].
pub fn new(
type_registry: &'a PortableRegistry,
root_mod: &'static str,
type_substitutes: HashMap<String, syn::TypePath>,
derives: GeneratedTypeDerives,
) -> Self {
let root_mod_ident = Ident::new(root_mod, Span::call_site());
Self {
types_mod_ident: root_mod_ident,
type_registry,
type_substitutes,
derives,
}
}
/// Generate a module containing all types defined in the supplied type registry.
pub fn generate_types_mod(&'a self) -> Module<'a> {
let mut root_mod =
Module::new(self.types_mod_ident.clone(), self.types_mod_ident.clone());
for (id, ty) in self.type_registry.types().iter().enumerate() {
if ty.ty().path().namespace().is_empty() {
// prelude types e.g. Option/Result have no namespace, so we don't generate them
continue
}
self.insert_type(
ty.ty().clone(),
id as u32,
ty.ty().path().namespace().to_vec(),
&self.types_mod_ident,
&mut root_mod,
)
}
root_mod
}
fn insert_type(
&'a self,
ty: Type<PortableForm>,
id: u32,
path: Vec<String>,
root_mod_ident: &Ident,
module: &mut Module<'a>,
) {
let joined_path = path.join("::");
if self.type_substitutes.contains_key(&joined_path) {
return
}
let segment = path.first().expect("path has at least one segment");
let mod_ident = Ident::new(segment, Span::call_site());
let child_mod = module
.children
.entry(mod_ident.clone())
.or_insert_with(|| Module::new(mod_ident, root_mod_ident.clone()));
if path.len() == 1 {
child_mod
.types
.insert(ty.path().clone(), TypeDefGen { ty, type_gen: self });
} else {
self.insert_type(ty, id, path[1..].to_vec(), root_mod_ident, child_mod)
}
}
/// # Panics
///
/// If no type with the given id found in the type registry.
pub fn resolve_type(&self, id: u32) -> Type<PortableForm> {
self.type_registry
.resolve(id)
.unwrap_or_else(|| panic!("No type with id {} found", id))
.clone()
}
/// # Panics
///
/// If no type with the given id found in the type registry.
pub fn resolve_type_path(
&self,
id: u32,
parent_type_params: &[TypeParameter],
) -> TypePath {
if let Some(parent_type_param) = parent_type_params
.iter()
.find(|tp| tp.concrete_type_id == id)
{
return TypePath::Parameter(parent_type_param.clone())
}
let mut ty = self.resolve_type(id);
if ty.path().ident() == Some("Cow".to_string()) {
ty = self.resolve_type(
ty.type_params()[0]
.ty()
.expect("type parameters to Cow are not expected to be skipped; qed")
.id(),
)
}
let params_type_ids = match ty.type_def() {
TypeDef::Array(arr) => vec![arr.type_param().id()],
TypeDef::Sequence(seq) => vec![seq.type_param().id()],
TypeDef::Tuple(tuple) => tuple.fields().iter().map(|f| f.id()).collect(),
TypeDef::Compact(compact) => vec![compact.type_param().id()],
TypeDef::BitSequence(seq) => {
vec![seq.bit_order_type().id(), seq.bit_store_type().id()]
}
_ => {
ty.type_params()
.iter()
.filter_map(|f| f.ty().map(|f| f.id()))
.collect()
}
};
let params = params_type_ids
.iter()
.map(|tp| self.resolve_type_path(*tp, parent_type_params))
.collect::<Vec<_>>();
let joined_path = ty.path().segments().join("::");
if let Some(substitute_type_path) = self.type_substitutes.get(&joined_path) {
TypePath::Substitute(TypePathSubstitute {
path: substitute_type_path.clone(),
params,
})
} else {
TypePath::Type(TypePathType {
ty,
params,
root_mod_ident: self.types_mod_ident.clone(),
})
}
}
/// Returns the derives with which all generated type will be decorated.
pub fn derives(&self) -> &GeneratedTypeDerives {
&self.derives
}
}
#[derive(Debug)]
pub struct Module<'a> {
name: Ident,
root_mod: Ident,
children: BTreeMap<Ident, Module<'a>>,
types: BTreeMap<scale_info::Path<scale_info::form::PortableForm>, TypeDefGen<'a>>,
}
impl<'a> ToTokens for Module<'a> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let name = &self.name;
let root_mod = &self.root_mod;
let modules = self.children.values();
let types = self.types.values().clone();
tokens.extend(quote! {
pub mod #name {
use super::#root_mod;
#( #modules )*
#( #types )*
}
})
}
}
impl<'a> Module<'a> {
pub fn new(name: Ident, root_mod: Ident) -> Self {
Self {
name,
root_mod,
children: BTreeMap::new(),
types: BTreeMap::new(),
}
}
/// Returns the module ident.
pub fn ident(&self) -> &Ident {
&self.name
}
}
+794
View File
@@ -0,0 +1,794 @@
// Copyright 2019-2021 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::*;
use pretty_assertions::assert_eq;
use scale_info::{
meta_type,
Registry,
TypeInfo,
};
const MOD_PATH: &'static [&'static str] = &["subxt_codegen", "types", "tests"];
fn get_mod<'a>(module: &'a Module, path_segs: &[&'static str]) -> Option<&'a Module<'a>> {
let (mod_name, rest) = path_segs.split_first()?;
let mod_ident = Ident::new(mod_name, Span::call_site());
let module = module.children.get(&mod_ident)?;
if rest.is_empty() {
Some(module)
} else {
get_mod(module, rest)
}
}
#[test]
fn generate_struct_with_primitives() {
#[allow(unused)]
#[derive(TypeInfo)]
struct S {
a: bool,
b: u32,
c: char,
}
let mut registry = Registry::new();
registry.register_type(&meta_type::<S>());
let portable_types: PortableRegistry = registry.into();
let type_gen = TypeGenerator::new(
&portable_types,
"root",
Default::default(),
Default::default(),
);
let types = type_gen.generate_types_mod();
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
tests_mod.into_token_stream().to_string(),
quote! {
pub mod tests {
use super::root;
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
pub struct S {
pub a: ::core::primitive::bool,
pub b: ::core::primitive::u32,
pub c: ::core::primitive::char,
}
}
}
.to_string()
)
}
#[test]
fn generate_struct_with_a_struct_field() {
#[allow(unused)]
#[derive(TypeInfo)]
struct Parent {
a: bool,
b: Child,
}
#[allow(unused)]
#[derive(TypeInfo)]
struct Child {
a: i32,
}
let mut registry = Registry::new();
registry.register_type(&meta_type::<Parent>());
let portable_types: PortableRegistry = registry.into();
let type_gen = TypeGenerator::new(
&portable_types,
"root",
Default::default(),
Default::default(),
);
let types = type_gen.generate_types_mod();
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
tests_mod.into_token_stream().to_string(),
quote! {
pub mod tests {
use super::root;
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
pub struct Child {
pub a: ::core::primitive::i32,
}
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
pub struct Parent {
pub a: ::core::primitive::bool,
pub b: root::subxt_codegen::types::tests::Child,
}
}
}
.to_string()
)
}
#[test]
fn generate_tuple_struct() {
#[allow(unused)]
#[derive(TypeInfo)]
struct Parent(bool, Child);
#[allow(unused)]
#[derive(TypeInfo)]
struct Child(i32);
let mut registry = Registry::new();
registry.register_type(&meta_type::<Parent>());
let portable_types: PortableRegistry = registry.into();
let type_gen = TypeGenerator::new(
&portable_types,
"root",
Default::default(),
Default::default(),
);
let types = type_gen.generate_types_mod();
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
tests_mod.into_token_stream().to_string(),
quote! {
pub mod tests {
use super::root;
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
pub struct Child(pub ::core::primitive::i32,);
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
pub struct Parent(pub ::core::primitive::bool, pub root::subxt_codegen::types::tests::Child,);
}
}
.to_string()
)
}
#[test]
fn derive_compact_as_for_uint_wrapper_structs() {
#[allow(unused)]
#[derive(TypeInfo)]
struct Su8 {
a: u8,
}
#[allow(unused)]
#[derive(TypeInfo)]
struct TSu8(u8);
#[allow(unused)]
#[derive(TypeInfo)]
struct Su16 {
a: u16,
}
#[allow(unused)]
#[derive(TypeInfo)]
struct TSu16(u16);
#[allow(unused)]
#[derive(TypeInfo)]
struct Su32 {
a: u32,
}
#[allow(unused)]
#[derive(TypeInfo)]
struct TSu32(u32);
#[allow(unused)]
#[derive(TypeInfo)]
struct Su64 {
a: u64,
}
#[allow(unused)]
#[derive(TypeInfo)]
struct TSu64(u64);
#[allow(unused)]
#[derive(TypeInfo)]
struct Su128 {
a: u128,
}
#[allow(unused)]
#[derive(TypeInfo)]
struct TSu128(u128);
let mut registry = Registry::new();
registry.register_type(&meta_type::<Su8>());
registry.register_type(&meta_type::<TSu8>());
registry.register_type(&meta_type::<Su16>());
registry.register_type(&meta_type::<TSu16>());
registry.register_type(&meta_type::<Su32>());
registry.register_type(&meta_type::<TSu32>());
registry.register_type(&meta_type::<Su64>());
registry.register_type(&meta_type::<TSu64>());
registry.register_type(&meta_type::<Su128>());
registry.register_type(&meta_type::<TSu128>());
let portable_types: PortableRegistry = registry.into();
let type_gen = TypeGenerator::new(
&portable_types,
"root",
Default::default(),
Default::default(),
);
let types = type_gen.generate_types_mod();
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
tests_mod.into_token_stream().to_string(),
quote! {
pub mod tests {
use super::root;
#[derive(::subxt::codec::CompactAs)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
pub struct Su128 { pub a: ::core::primitive::u128, }
#[derive(::subxt::codec::CompactAs)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
pub struct Su16 { pub a: ::core::primitive::u16, }
#[derive(::subxt::codec::CompactAs)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
pub struct Su32 { pub a: ::core::primitive::u32, }
#[derive(::subxt::codec::CompactAs)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
pub struct Su64 { pub a: ::core::primitive::u64, }
#[derive(::subxt::codec::CompactAs)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
pub struct Su8 { pub a: ::core::primitive::u8, }
#[derive(::subxt::codec::CompactAs)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
pub struct TSu128(pub ::core::primitive::u128,);
#[derive(::subxt::codec::CompactAs)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
pub struct TSu16(pub ::core::primitive::u16,);
#[derive(::subxt::codec::CompactAs)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
pub struct TSu32(pub ::core::primitive::u32,);
#[derive(::subxt::codec::CompactAs)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
pub struct TSu64(pub ::core::primitive::u64,);
#[derive(::subxt::codec::CompactAs)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
pub struct TSu8(pub ::core::primitive::u8,);
}
}
.to_string()
)
}
#[test]
fn generate_enum() {
#[allow(unused)]
#[derive(TypeInfo)]
enum E {
A,
B(bool),
C { a: u32 },
}
let mut registry = Registry::new();
registry.register_type(&meta_type::<E>());
let portable_types: PortableRegistry = registry.into();
let type_gen = TypeGenerator::new(
&portable_types,
"root",
Default::default(),
Default::default(),
);
let types = type_gen.generate_types_mod();
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
tests_mod.into_token_stream().to_string(),
quote! {
pub mod tests {
use super::root;
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
pub enum E {
A,
B (::core::primitive::bool,),
C { a: ::core::primitive::u32, },
}
}
}
.to_string()
)
}
#[test]
fn generate_array_field() {
#[allow(unused)]
#[derive(TypeInfo)]
struct S {
a: [u8; 32],
}
let mut registry = Registry::new();
registry.register_type(&meta_type::<S>());
let portable_types: PortableRegistry = registry.into();
let type_gen = TypeGenerator::new(
&portable_types,
"root",
Default::default(),
Default::default(),
);
let types = type_gen.generate_types_mod();
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
tests_mod.into_token_stream().to_string(),
quote! {
pub mod tests {
use super::root;
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
pub struct S {
pub a: [::core::primitive::u8; 32usize],
}
}
}
.to_string()
)
}
#[test]
fn option_fields() {
#[allow(unused)]
#[derive(TypeInfo)]
struct S {
a: Option<bool>,
b: Option<u32>,
}
let mut registry = Registry::new();
registry.register_type(&meta_type::<S>());
let portable_types: PortableRegistry = registry.into();
let type_gen = TypeGenerator::new(
&portable_types,
"root",
Default::default(),
Default::default(),
);
let types = type_gen.generate_types_mod();
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
tests_mod.into_token_stream().to_string(),
quote! {
pub mod tests {
use super::root;
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
pub struct S {
pub a: ::core::option::Option<::core::primitive::bool>,
pub b: ::core::option::Option<::core::primitive::u32>,
}
}
}
.to_string()
)
}
#[test]
fn box_fields_struct() {
use std::boxed::Box;
#[allow(unused)]
#[derive(TypeInfo)]
struct S {
a: std::boxed::Box<bool>,
b: Box<u32>,
}
let mut registry = Registry::new();
registry.register_type(&meta_type::<S>());
let portable_types: PortableRegistry = registry.into();
let type_gen = TypeGenerator::new(
&portable_types,
"root",
Default::default(),
Default::default(),
);
let types = type_gen.generate_types_mod();
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
tests_mod.into_token_stream().to_string(),
quote! {
pub mod tests {
use super::root;
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
pub struct S {
pub a: ::std::boxed::Box<::core::primitive::bool>,
pub b: ::std::boxed::Box<::core::primitive::u32>,
}
}
}
.to_string()
)
}
#[test]
fn box_fields_enum() {
use std::boxed::Box;
#[allow(unused)]
#[derive(TypeInfo)]
enum E {
A(Box<bool>),
B { a: Box<u32> },
}
let mut registry = Registry::new();
registry.register_type(&meta_type::<E>());
let portable_types: PortableRegistry = registry.into();
let type_gen = TypeGenerator::new(
&portable_types,
"root",
Default::default(),
Default::default(),
);
let types = type_gen.generate_types_mod();
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
tests_mod.into_token_stream().to_string(),
quote! {
pub mod tests {
use super::root;
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
pub enum E {
A(::std::boxed::Box<::core::primitive::bool>,),
B { a: ::std::boxed::Box<::core::primitive::u32>, },
}
}
}
.to_string()
)
}
#[test]
fn range_fields() {
#[allow(unused)]
#[derive(TypeInfo)]
struct S {
a: core::ops::Range<u32>,
b: core::ops::RangeInclusive<u32>,
}
let mut registry = Registry::new();
registry.register_type(&meta_type::<S>());
let portable_types: PortableRegistry = registry.into();
let type_gen = TypeGenerator::new(
&portable_types,
"root",
Default::default(),
Default::default(),
);
let types = type_gen.generate_types_mod();
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
tests_mod.into_token_stream().to_string(),
quote! {
pub mod tests {
use super::root;
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
pub struct S {
pub a: ::core::ops::Range<::core::primitive::u32>,
pub b: ::core::ops::RangeInclusive<::core::primitive::u32>,
}
}
}
.to_string()
)
}
#[test]
fn generics() {
#[allow(unused)]
#[derive(TypeInfo)]
struct Foo<T> {
a: T,
}
#[allow(unused)]
#[derive(TypeInfo)]
struct Bar {
b: Foo<u32>,
c: Foo<u8>,
}
let mut registry = Registry::new();
registry.register_type(&meta_type::<Bar>());
let portable_types: PortableRegistry = registry.into();
let type_gen = TypeGenerator::new(
&portable_types,
"root",
Default::default(),
Default::default(),
);
let types = type_gen.generate_types_mod();
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
tests_mod.into_token_stream().to_string(),
quote! {
pub mod tests {
use super::root;
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
pub struct Bar {
pub b: root::subxt_codegen::types::tests::Foo<::core::primitive::u32>,
pub c: root::subxt_codegen::types::tests::Foo<::core::primitive::u8>,
}
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
pub struct Foo<_0> {
pub a: _0,
}
}
}
.to_string()
)
}
#[test]
fn generics_nested() {
#[allow(unused)]
#[derive(TypeInfo)]
struct Foo<T, U> {
a: T,
b: Option<(T, U)>,
}
#[allow(unused)]
#[derive(TypeInfo)]
struct Bar<T> {
b: Foo<T, u32>,
}
let mut registry = Registry::new();
registry.register_type(&meta_type::<Bar<bool>>());
let portable_types: PortableRegistry = registry.into();
let type_gen = TypeGenerator::new(
&portable_types,
"root",
Default::default(),
Default::default(),
);
let types = type_gen.generate_types_mod();
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
tests_mod.into_token_stream().to_string(),
quote! {
pub mod tests {
use super::root;
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
pub struct Bar<_0> {
pub b: root::subxt_codegen::types::tests::Foo<_0, ::core::primitive::u32>,
}
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
pub struct Foo<_0, _1> {
pub a: _0,
pub b: ::core::option::Option<(_0, _1,)>,
}
}
}
.to_string()
)
}
#[test]
fn generate_bitvec() {
use bitvec::{
order::{
Lsb0,
Msb0,
},
vec::BitVec,
};
#[allow(unused)]
#[derive(TypeInfo)]
struct S {
lsb: BitVec<Lsb0, u8>,
msb: BitVec<Msb0, u16>,
}
let mut registry = Registry::new();
registry.register_type(&meta_type::<S>());
let portable_types: PortableRegistry = registry.into();
let type_gen = TypeGenerator::new(
&portable_types,
"root",
Default::default(),
Default::default(),
);
let types = type_gen.generate_types_mod();
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
tests_mod.into_token_stream().to_string(),
quote! {
pub mod tests {
use super::root;
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
pub struct S {
pub lsb: ::subxt::bitvec::vec::BitVec<root::bitvec::order::Lsb0, ::core::primitive::u8>,
pub msb: ::subxt::bitvec::vec::BitVec<root::bitvec::order::Msb0, ::core::primitive::u16>,
}
}
}
.to_string()
)
}
#[test]
fn generics_with_alias_adds_phantom_data_marker() {
trait Trait {
type Type;
}
impl Trait for bool {
type Type = u32;
}
type Foo<T> = <T as Trait>::Type;
type Bar<T, U> = (<T as Trait>::Type, <U as Trait>::Type);
#[allow(unused)]
#[derive(TypeInfo)]
struct NamedFields<T: Trait> {
b: Foo<T>,
}
#[allow(unused)]
#[derive(TypeInfo)]
struct UnnamedFields<T: Trait, U: Trait>(Bar<T, U>);
let mut registry = Registry::new();
registry.register_type(&meta_type::<NamedFields<bool>>());
registry.register_type(&meta_type::<UnnamedFields<bool, bool>>());
let portable_types: PortableRegistry = registry.into();
let type_gen = TypeGenerator::new(
&portable_types,
"root",
Default::default(),
Default::default(),
);
let types = type_gen.generate_types_mod();
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
tests_mod.into_token_stream().to_string(),
quote! {
pub mod tests {
use super::root;
#[derive(::subxt::codec::CompactAs)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
pub struct NamedFields<_0> {
pub b: ::core::primitive::u32,
#[codec(skip)] pub __subxt_unused_type_params: ::core::marker::PhantomData<_0>,
}
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
pub struct UnnamedFields<_0, _1> (
pub (::core::primitive::u32, ::core::primitive::u32,),
#[codec(skip)] pub ::core::marker::PhantomData<(_0, _1)>,
);
}
}
.to_string()
)
}
#[test]
fn modules() {
mod modules {
pub mod a {
#[allow(unused)]
#[derive(scale_info::TypeInfo)]
pub struct Foo {}
pub mod b {
#[allow(unused)]
#[derive(scale_info::TypeInfo)]
pub struct Bar {
a: super::Foo,
}
}
}
pub mod c {
#[allow(unused)]
#[derive(scale_info::TypeInfo)]
pub struct Foo {
a: super::a::b::Bar,
}
}
}
let mut registry = Registry::new();
registry.register_type(&meta_type::<modules::c::Foo>());
let portable_types: PortableRegistry = registry.into();
let type_gen = TypeGenerator::new(
&portable_types,
"root",
Default::default(),
Default::default(),
);
let types = type_gen.generate_types_mod();
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
tests_mod.into_token_stream().to_string(),
quote! {
pub mod tests {
use super::root;
pub mod modules {
use super::root;
pub mod a {
use super::root;
pub mod b {
use super::root;
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
pub struct Bar {
pub a: root::subxt_codegen::types::tests::modules::a::Foo,
}
}
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
pub struct Foo {}
}
pub mod c {
use super::root;
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
pub struct Foo {
pub a: root::subxt_codegen::types::tests::modules::a::b::Bar,
}
}
}
}
}
.to_string()
)
}
+325
View File
@@ -0,0 +1,325 @@
// Copyright 2019-2021 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::{
TypeGenerator,
TypeParameter,
TypePath,
};
use proc_macro2::TokenStream;
use quote::{
format_ident,
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`].
///
/// Field type paths are resolved via the `TypeGenerator`, which contains the registry of all
/// 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>,
}
impl<'a> quote::ToTokens for TypeDefGen<'a> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let type_params = self
.ty
.type_params()
.iter()
.enumerate()
.filter_map(|(i, tp)| {
match tp.ty() {
Some(ty) => {
let tp_name = format_ident!("_{}", i);
Some(TypeParameter {
concrete_type_id: ty.id(),
name: tp_name,
})
}
None => None,
}
})
.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 derives = self.type_gen.derives();
match self.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);
}
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();
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)
};
variants.push(quote! { #variant_name #fields });
let unused_params_set = unused_type_params.iter().cloned().collect();
let used_params = type_params_set.difference(&unused_params_set);
for used_param in used_params {
used_type_params.insert(used_param.clone());
}
}
let unused_type_params = type_params_set
.difference(&used_type_params)
.cloned()
.collect::<Vec<_>>();
if !unused_type_params.is_empty() {
let phantom = Self::phantom_data(&unused_type_params);
variants.push(quote! {
__Ignore(#phantom)
})
}
let ty_toks = quote! {
#derives
pub enum #type_name {
#( #variants, )*
}
};
tokens.extend(ty_toks);
}
_ => (), // 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)| {
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 }
}
}
})
.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 = &params[0];
quote! { #param }
} else {
quote! { ( #( #params ), * ) }
};
quote! ( ::core::marker::PhantomData<#params> )
}
}
+253
View File
@@ -0,0 +1,253 @@
// Copyright 2019-2021 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 proc_macro2::{
Ident,
TokenStream,
};
use quote::{
format_ident,
quote,
};
use scale_info::{
form::PortableForm,
Type,
TypeDef,
TypeDefPrimitive,
};
use std::collections::HashSet;
use syn::parse_quote;
#[derive(Clone, Debug)]
pub enum TypePath {
Parameter(TypeParameter),
Type(TypePathType),
Substitute(TypePathSubstitute),
}
impl quote::ToTokens for TypePath {
fn to_tokens(&self, tokens: &mut TokenStream) {
let syn_type = self.to_syn_type();
syn_type.to_tokens(tokens)
}
}
impl TypePath {
pub(crate) fn to_syn_type(&self) -> syn::Type {
match self {
TypePath::Parameter(ty_param) => syn::Type::Path(parse_quote! { #ty_param }),
TypePath::Type(ty) => ty.to_syn_type(),
TypePath::Substitute(sub) => sub.to_syn_type(),
}
}
pub(crate) fn is_compact(&self) -> bool {
matches!(self, Self::Type(ty) if ty.is_compact())
}
/// Returns the type parameters in a path which are inherited from the containing type.
///
/// # Example
///
/// ```rust
/// struct S<T> {
/// a: Vec<Option<T>>, // the parent type param here is `T`
/// }
/// ```
pub fn parent_type_params(&self, acc: &mut HashSet<TypeParameter>) {
match self {
Self::Parameter(type_parameter) => {
acc.insert(type_parameter.clone());
}
Self::Type(type_path) => type_path.parent_type_params(acc),
Self::Substitute(sub) => sub.parent_type_params(acc),
}
}
}
#[derive(Clone, Debug)]
pub struct TypePathType {
pub(super) ty: Type<PortableForm>,
pub(super) params: Vec<TypePath>,
pub(super) root_mod_ident: Ident,
}
impl TypePathType {
pub(crate) fn is_compact(&self) -> bool {
matches!(self.ty.type_def(), TypeDef::Compact(_))
}
fn to_syn_type(&self) -> syn::Type {
let params = &self.params;
match self.ty.type_def() {
TypeDef::Composite(_) | TypeDef::Variant(_) => {
let path_segments = self.ty.path().segments();
let ty_path: syn::TypePath = match path_segments {
[] => panic!("Type has no ident"),
[ident] => {
// paths to prelude types
match ident.as_str() {
"Option" => parse_quote!(::core::option::Option),
"Result" => parse_quote!(::core::result::Result),
"Cow" => parse_quote!(::std::borrow::Cow),
"BTreeMap" => parse_quote!(::std::collections::BTreeMap),
"BTreeSet" => parse_quote!(::std::collections::BTreeSet),
"Range" => parse_quote!(::core::ops::Range),
"RangeInclusive" => parse_quote!(::core::ops::RangeInclusive),
ident => panic!("Unknown prelude type '{}'", ident),
}
}
_ => {
// paths to generated types in the root types module
let mut ty_path = path_segments
.iter()
.map(|s| syn::PathSegment::from(format_ident!("{}", s)))
.collect::<syn::punctuated::Punctuated<
syn::PathSegment,
syn::Token![::],
>>();
ty_path.insert(
0,
syn::PathSegment::from(self.root_mod_ident.clone()),
);
parse_quote!( #ty_path )
}
};
let params = &self.params;
let path = if params.is_empty() {
parse_quote! { #ty_path }
} else {
parse_quote! { #ty_path< #( #params ),* > }
};
syn::Type::Path(path)
}
TypeDef::Sequence(_) => {
let type_param = &self.params[0];
let type_path = parse_quote! { ::std::vec::Vec<#type_param> };
syn::Type::Path(type_path)
}
TypeDef::Array(array) => {
let array_type = &self.params[0];
let array_len = array.len() as usize;
let array = parse_quote! { [#array_type; #array_len] };
syn::Type::Array(array)
}
TypeDef::Tuple(_) => {
let tuple = parse_quote! { (#( # params, )* ) };
syn::Type::Tuple(tuple)
}
TypeDef::Primitive(primitive) => {
let path = match primitive {
TypeDefPrimitive::Bool => parse_quote!(::core::primitive::bool),
TypeDefPrimitive::Char => parse_quote!(::core::primitive::char),
TypeDefPrimitive::Str => parse_quote!(::std::string::String),
TypeDefPrimitive::U8 => parse_quote!(::core::primitive::u8),
TypeDefPrimitive::U16 => parse_quote!(::core::primitive::u16),
TypeDefPrimitive::U32 => parse_quote!(::core::primitive::u32),
TypeDefPrimitive::U64 => parse_quote!(::core::primitive::u64),
TypeDefPrimitive::U128 => parse_quote!(::core::primitive::u128),
TypeDefPrimitive::U256 => unimplemented!("not a rust primitive"),
TypeDefPrimitive::I8 => parse_quote!(::core::primitive::i8),
TypeDefPrimitive::I16 => parse_quote!(::core::primitive::i16),
TypeDefPrimitive::I32 => parse_quote!(::core::primitive::i32),
TypeDefPrimitive::I64 => parse_quote!(::core::primitive::i64),
TypeDefPrimitive::I128 => parse_quote!(::core::primitive::i128),
TypeDefPrimitive::I256 => unimplemented!("not a rust primitive"),
};
syn::Type::Path(path)
}
TypeDef::Compact(_) => {
let compact_type = &self.params[0];
syn::Type::Path(parse_quote! ( #compact_type ))
}
TypeDef::BitSequence(_) => {
let bit_order_type = &self.params[0];
let bit_store_type = &self.params[1];
let type_path = parse_quote! { ::subxt::bitvec::vec::BitVec<#bit_order_type, #bit_store_type> };
syn::Type::Path(type_path)
}
}
}
/// Returns the type parameters in a path which are inherited from the containing type.
///
/// # Example
///
/// ```rust
/// struct S<T> {
/// a: Vec<Option<T>>, // the parent type param here is `T`
/// }
/// ```
fn parent_type_params(&self, acc: &mut HashSet<TypeParameter>) {
for p in &self.params {
p.parent_type_params(acc);
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct TypeParameter {
pub(super) concrete_type_id: u32,
pub(super) name: proc_macro2::Ident,
}
impl quote::ToTokens for TypeParameter {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.name.to_tokens(tokens)
}
}
#[derive(Clone, Debug)]
pub struct TypePathSubstitute {
pub(super) path: syn::TypePath,
pub(super) params: Vec<TypePath>,
}
impl quote::ToTokens for TypePathSubstitute {
fn to_tokens(&self, tokens: &mut TokenStream) {
if self.params.is_empty() {
self.path.to_tokens(tokens)
} else {
let substitute_path = &self.path;
let params = &self.params;
tokens.extend(quote! {
#substitute_path< #( #params ),* >
})
}
}
}
impl TypePathSubstitute {
fn parent_type_params(&self, acc: &mut HashSet<TypeParameter>) {
for p in &self.params {
p.parent_type_params(acc);
}
}
fn to_syn_type(&self) -> syn::Type {
if self.params.is_empty() {
syn::Type::Path(self.path.clone())
} else {
let substitute_path = &self.path;
let params = &self.params;
parse_quote! ( #substitute_path< #( #params ),* > )
}
}
}