mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-25 01:17:57 +00:00
Add 'Static' type and improve type substitution codegen to accept it (#886)
* Add Static type which defers to Encode/Decode and impls EncodeAsType/DecodeAsType * rename to static_type and impl Deref/Mut * Improve type substitution in codegen so that concrete types can be swapped in * A couple of comment tweaks and no need for a macro export * Extend type substitution logic to work recursively on destination type * cargo fmt * Fix a couple of comments * update ui test outpuot * Add docs and missing_docs lint * Add test for replacing multiple of Ident * Update codegen/src/error.rs Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com> * update copyright year and fix ui test * simplify another error --------- Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
|
||||
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
|
||||
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
|
||||
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
|
||||
+4
-63
@@ -1,4 +1,4 @@
|
||||
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
|
||||
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
@@ -12,80 +12,21 @@ mod storage;
|
||||
use subxt_metadata::get_metadata_per_pallet_hash;
|
||||
|
||||
use super::DerivesRegistry;
|
||||
use crate::error::CodegenError;
|
||||
use crate::{
|
||||
ir,
|
||||
types::{CompositeDef, CompositeDefFields, TypeGenerator, TypeSubstitutes},
|
||||
utils::{fetch_metadata_bytes_blocking, FetchMetadataError, Uri},
|
||||
utils::{fetch_metadata_bytes_blocking, Uri},
|
||||
CratePath,
|
||||
};
|
||||
use codec::Decode;
|
||||
use frame_metadata::{v14::RuntimeMetadataV14, RuntimeMetadata, RuntimeMetadataPrefixed};
|
||||
use heck::ToSnakeCase as _;
|
||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{format_ident, quote};
|
||||
use std::{fs, io::Read, path, string::ToString};
|
||||
use syn::parse_quote;
|
||||
|
||||
/// Error returned when the Codegen cannot generate the runtime API.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum CodegenError {
|
||||
/// The given metadata type could not be found.
|
||||
#[error("Could not find type with ID {0} in the type registry; please raise a support issue.")]
|
||||
TypeNotFound(u32),
|
||||
/// Cannot fetch the metadata bytes.
|
||||
#[error("Failed to fetch metadata, make sure that you're pointing at a node which is providing V14 metadata: {0}")]
|
||||
Fetch(#[from] FetchMetadataError),
|
||||
/// Failed IO for the metadata file.
|
||||
#[error("Failed IO for {0}, make sure that you are providing the correct file path for metadata V14: {1}")]
|
||||
Io(String, std::io::Error),
|
||||
/// Cannot decode the metadata bytes.
|
||||
#[error("Could not decode metadata, only V14 metadata is supported: {0}")]
|
||||
Decode(#[from] codec::Error),
|
||||
/// Out of line modules are not supported.
|
||||
#[error("Out-of-line subxt modules are not supported, make sure you are providing a body to your module: pub mod polkadot {{ ... }}")]
|
||||
InvalidModule(Span),
|
||||
/// Expected named or unnamed fields.
|
||||
#[error("Fields should either be all named or all unnamed, make sure you are providing a valid metadata V14: {0}")]
|
||||
InvalidFields(String),
|
||||
/// Substitute types must have a valid path.
|
||||
#[error("Substitute types must have a valid path")]
|
||||
EmptySubstitutePath(Span),
|
||||
/// Invalid type path.
|
||||
#[error("Invalid type path {0}: {1}")]
|
||||
InvalidTypePath(String, syn::Error),
|
||||
/// Metadata for constant could not be found.
|
||||
#[error("Metadata for constant entry {0}_{1} could not be found. Make sure you are providing a valid metadata V14")]
|
||||
MissingConstantMetadata(String, String),
|
||||
/// Metadata for storage could not be found.
|
||||
#[error("Metadata for storage entry {0}_{1} could not be found. Make sure you are providing a valid metadata V14")]
|
||||
MissingStorageMetadata(String, String),
|
||||
/// Metadata for call could not be found.
|
||||
#[error("Metadata for call entry {0}_{1} could not be found. Make sure you are providing a valid metadata V14")]
|
||||
MissingCallMetadata(String, String),
|
||||
/// Call variant must have all named fields.
|
||||
#[error("Call variant for type {0} must have all named fields. Make sure you are providing a valid metadata V14")]
|
||||
InvalidCallVariant(u32),
|
||||
/// Type should be an variant/enum.
|
||||
#[error(
|
||||
"{0} type should be an variant/enum type. Make sure you are providing a valid metadata V14"
|
||||
)]
|
||||
InvalidType(String),
|
||||
}
|
||||
|
||||
impl CodegenError {
|
||||
/// Render the error as an invocation of syn::compile_error!.
|
||||
pub fn into_compile_error(self) -> TokenStream2 {
|
||||
let msg = self.to_string();
|
||||
let span = match self {
|
||||
Self::InvalidModule(span) => span,
|
||||
Self::EmptySubstitutePath(span) => span,
|
||||
Self::InvalidTypePath(_, err) => err.span(),
|
||||
_ => proc_macro2::Span::call_site(),
|
||||
};
|
||||
syn::Error::new(span, msg).into_compile_error()
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates the API for interacting with a Substrate runtime.
|
||||
///
|
||||
/// # Arguments
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
|
||||
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
@@ -92,7 +92,7 @@ fn generate_storage_entry_fns(
|
||||
let keys = fields
|
||||
.iter()
|
||||
.map(|(field_name, _)| {
|
||||
quote!( #crate_path::storage::address::StaticStorageMapKey::new(#field_name.borrow()) )
|
||||
quote!( #crate_path::storage::address::make_static_storage_map_key(#field_name.borrow()) )
|
||||
});
|
||||
let key_impl = quote! {
|
||||
vec![ #( #keys ),* ]
|
||||
@@ -105,7 +105,7 @@ fn generate_storage_entry_fns(
|
||||
let ty_path = type_gen.resolve_type_path(key.id());
|
||||
let fields = vec![(format_ident!("_0"), ty_path)];
|
||||
let key_impl = quote! {
|
||||
vec![ #crate_path::storage::address::StaticStorageMapKey::new(_0.borrow()) ]
|
||||
vec![ #crate_path::storage::address::make_static_storage_map_key(_0.borrow()) ]
|
||||
};
|
||||
(fields, key_impl)
|
||||
}
|
||||
@@ -134,7 +134,7 @@ fn generate_storage_entry_fns(
|
||||
|
||||
let key_args = fields.iter().map(|(field_name, field_type)| {
|
||||
// The field type is translated from `std::vec::Vec<T>` to `[T]`. We apply
|
||||
// AsRef to all types, so this just makes it a little more ergonomic.
|
||||
// Borrow to all types, so this just makes it a little more ergonomic.
|
||||
//
|
||||
// TODO [jsdw]: Support mappings like `String -> str` too for better borrow
|
||||
// ergonomics.
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
//! Errors that can be emitted from codegen.
|
||||
|
||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||
|
||||
/// Error returned when the Codegen cannot generate the runtime API.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum CodegenError {
|
||||
/// The given metadata type could not be found.
|
||||
#[error("Could not find type with ID {0} in the type registry; please raise a support issue.")]
|
||||
TypeNotFound(u32),
|
||||
/// Cannot fetch the metadata bytes.
|
||||
#[error("Failed to fetch metadata, make sure that you're pointing at a node which is providing V14 metadata: {0}")]
|
||||
Fetch(#[from] FetchMetadataError),
|
||||
/// Failed IO for the metadata file.
|
||||
#[error("Failed IO for {0}, make sure that you are providing the correct file path for metadata V14: {1}")]
|
||||
Io(String, std::io::Error),
|
||||
/// Cannot decode the metadata bytes.
|
||||
#[error("Could not decode metadata, only V14 metadata is supported: {0}")]
|
||||
Decode(#[from] codec::Error),
|
||||
/// Out of line modules are not supported.
|
||||
#[error("Out-of-line subxt modules are not supported, make sure you are providing a body to your module: pub mod polkadot {{ ... }}")]
|
||||
InvalidModule(Span),
|
||||
/// Expected named or unnamed fields.
|
||||
#[error("Fields should either be all named or all unnamed, make sure you are providing a valid metadata V14: {0}")]
|
||||
InvalidFields(String),
|
||||
/// Substitute types must have a valid path.
|
||||
#[error("Type substitution error: {0}")]
|
||||
TypeSubstitutionError(#[from] TypeSubstitutionError),
|
||||
/// Invalid type path.
|
||||
#[error("Invalid type path {0}: {1}")]
|
||||
InvalidTypePath(String, syn::Error),
|
||||
/// Metadata for constant could not be found.
|
||||
#[error("Metadata for constant entry {0}_{1} could not be found. Make sure you are providing a valid metadata V14")]
|
||||
MissingConstantMetadata(String, String),
|
||||
/// Metadata for storage could not be found.
|
||||
#[error("Metadata for storage entry {0}_{1} could not be found. Make sure you are providing a valid metadata V14")]
|
||||
MissingStorageMetadata(String, String),
|
||||
/// Metadata for call could not be found.
|
||||
#[error("Metadata for call entry {0}_{1} could not be found. Make sure you are providing a valid metadata V14")]
|
||||
MissingCallMetadata(String, String),
|
||||
/// Call variant must have all named fields.
|
||||
#[error("Call variant for type {0} must have all named fields. Make sure you are providing a valid metadata V14")]
|
||||
InvalidCallVariant(u32),
|
||||
/// Type should be an variant/enum.
|
||||
#[error(
|
||||
"{0} type should be an variant/enum type. Make sure you are providing a valid metadata V14"
|
||||
)]
|
||||
InvalidType(String),
|
||||
}
|
||||
|
||||
impl CodegenError {
|
||||
/// Fetch the location for this error.
|
||||
// Todo: Probably worth storing location outside of the variant,
|
||||
// so that there's a common way to set a location for some error.
|
||||
fn get_location(&self) -> Span {
|
||||
match self {
|
||||
Self::InvalidModule(span) => *span,
|
||||
Self::TypeSubstitutionError(err) => err.get_location(),
|
||||
Self::InvalidTypePath(_, err) => err.span(),
|
||||
_ => proc_macro2::Span::call_site(),
|
||||
}
|
||||
}
|
||||
/// Render the error as an invocation of syn::compile_error!.
|
||||
pub fn into_compile_error(self) -> TokenStream2 {
|
||||
let msg = self.to_string();
|
||||
let span = self.get_location();
|
||||
syn::Error::new(span, msg).into_compile_error()
|
||||
}
|
||||
}
|
||||
|
||||
/// Error attempting to load metadata.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum FetchMetadataError {
|
||||
#[error("Cannot decode hex value: {0}")]
|
||||
DecodeError(#[from] hex::FromHexError),
|
||||
#[error("Request error: {0}")]
|
||||
RequestError(#[from] jsonrpsee::core::Error),
|
||||
#[error("'{0}' not supported, supported URI schemes are http, https, ws or wss.")]
|
||||
InvalidScheme(String),
|
||||
}
|
||||
|
||||
/// Error attempting to do type substitution.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum TypeSubstitutionError {
|
||||
/// Substitute "to" type must be an absolute path.
|
||||
#[error("`substitute_type(with = <path>)` must be a path prefixed with 'crate::' or '::'")]
|
||||
ExpectedAbsolutePath(Span),
|
||||
/// Substitute types must have a valid path.
|
||||
#[error("Substitute types must have a valid path.")]
|
||||
EmptySubstitutePath(Span),
|
||||
/// From/To substitution types should use angle bracket generics.
|
||||
#[error("Expected the from/to type generics to have the form 'Foo<A,B,C..>'.")]
|
||||
ExpectedAngleBracketGenerics(Span),
|
||||
/// Source substitute type must be an ident.
|
||||
#[error("Expected an ident like 'Foo' or 'A' to mark a type to be substituted.")]
|
||||
InvalidFromType(Span),
|
||||
/// Target type is invalid.
|
||||
#[error("Expected an ident like 'Foo' or an absolute concrete path like '::path::to::Bar' for the substitute type.")]
|
||||
InvalidToType(Span),
|
||||
/// Target ident doesn't correspond to any source type.
|
||||
#[error("Cannot find matching param on 'from' type.")]
|
||||
NoMatchingFromType(Span),
|
||||
}
|
||||
|
||||
impl TypeSubstitutionError {
|
||||
/// Fetch the location for this error.
|
||||
// Todo: Probably worth storing location outside of the variant,
|
||||
// so that there's a common way to set a location for some error.
|
||||
fn get_location(&self) -> Span {
|
||||
match self {
|
||||
TypeSubstitutionError::ExpectedAbsolutePath(span) => *span,
|
||||
TypeSubstitutionError::EmptySubstitutePath(span) => *span,
|
||||
TypeSubstitutionError::ExpectedAngleBracketGenerics(span) => *span,
|
||||
TypeSubstitutionError::InvalidFromType(span) => *span,
|
||||
TypeSubstitutionError::InvalidToType(span) => *span,
|
||||
TypeSubstitutionError::NoMatchingFromType(span) => *span,
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
|
||||
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use crate::api::CodegenError;
|
||||
use crate::error::CodegenError;
|
||||
use syn::token;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
|
||||
+5
-3
@@ -1,4 +1,4 @@
|
||||
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
|
||||
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
@@ -43,9 +43,10 @@
|
||||
//! println!("{}", runtime_api);
|
||||
//! ```
|
||||
|
||||
#![deny(unused_crate_dependencies)]
|
||||
#![deny(unused_crate_dependencies, missing_docs)]
|
||||
|
||||
mod api;
|
||||
mod error;
|
||||
mod ir;
|
||||
mod types;
|
||||
|
||||
@@ -54,7 +55,8 @@ pub mod utils;
|
||||
pub use self::{
|
||||
api::{
|
||||
generate_runtime_api_from_bytes, generate_runtime_api_from_path,
|
||||
generate_runtime_api_from_url, CodegenError, RuntimeGenerator,
|
||||
generate_runtime_api_from_url, RuntimeGenerator,
|
||||
},
|
||||
error::{CodegenError, TypeSubstitutionError},
|
||||
types::{CratePath, Derives, DerivesRegistry, Module, TypeGenerator, TypeSubstitutes},
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
|
||||
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use crate::api::CodegenError;
|
||||
use crate::error::CodegenError;
|
||||
|
||||
use super::{CratePath, Derives, Field, TypeDefParameters, TypeGenerator, TypeParameter, TypePath};
|
||||
use proc_macro2::TokenStream;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
|
||||
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
@@ -7,6 +7,9 @@ use syn::{parse_quote, Path};
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
/// A struct containing the derives that we'll be applying to types;
|
||||
/// a combination of some common derives for all types, plus type
|
||||
/// specific derives.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DerivesRegistry {
|
||||
default_derives: Derives,
|
||||
@@ -62,6 +65,8 @@ impl DerivesRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
/// A struct storing the set of derives and derive attributes that we'll apply
|
||||
/// to generated types.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Derives {
|
||||
derives: HashSet<syn::Path>,
|
||||
@@ -113,16 +118,19 @@ impl Derives {
|
||||
self.insert_derive(parse_quote!(#crate_path::ext::codec::CompactAs));
|
||||
}
|
||||
|
||||
pub fn append(&mut self, derives: impl Iterator<Item = syn::Path>) {
|
||||
/// Extend the set of derives by providing an iterator of paths to derive macros.
|
||||
pub fn extend(&mut self, derives: impl Iterator<Item = syn::Path>) {
|
||||
for derive in derives {
|
||||
self.insert_derive(derive)
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert a single derive.
|
||||
pub fn insert_derive(&mut self, derive: syn::Path) {
|
||||
self.derives.insert(derive);
|
||||
}
|
||||
|
||||
/// Insert a single attribute to be applied to types.
|
||||
pub fn insert_attribute(&mut self, attribute: syn::Attribute) {
|
||||
self.attributes.insert(attribute);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
|
||||
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
@@ -17,7 +17,7 @@ use quote::{quote, ToTokens};
|
||||
use scale_info::{form::PortableForm, PortableRegistry, Type, TypeDef};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::api::CodegenError;
|
||||
use crate::error::CodegenError;
|
||||
|
||||
pub use self::{
|
||||
composite_def::{CompositeDef, CompositeDefFieldType, CompositeDefFields},
|
||||
@@ -77,11 +77,12 @@ impl<'a> TypeGenerator<'a> {
|
||||
let path = ty.ty().path();
|
||||
// Don't generate a type if it was substituted - the target type might
|
||||
// not be in the type registry + our resolution already performs the substitution.
|
||||
if self.type_substitutes.for_path(path).is_some() {
|
||||
if self.type_substitutes.contains(path) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let namespace = path.namespace();
|
||||
|
||||
// prelude types e.g. Option/Result have no namespace, so we don't generate them
|
||||
if namespace.is_empty() {
|
||||
continue;
|
||||
@@ -163,7 +164,7 @@ impl<'a> TypeGenerator<'a> {
|
||||
.iter()
|
||||
.find(|tp| tp.concrete_type_id == id)
|
||||
{
|
||||
return TypePath::Parameter(parent_type_param.clone());
|
||||
return TypePath::from_parameter(parent_type_param.clone());
|
||||
}
|
||||
|
||||
let mut ty = self.resolve_type(id);
|
||||
@@ -188,17 +189,11 @@ impl<'a> TypeGenerator<'a> {
|
||||
|
||||
let ty = match ty.type_def() {
|
||||
TypeDef::Composite(_) | TypeDef::Variant(_) => {
|
||||
if let Some((path, params)) = self
|
||||
if let Some(ty) = self
|
||||
.type_substitutes
|
||||
.for_path_with_params(ty.path(), ¶ms)
|
||||
{
|
||||
TypePathType::Path {
|
||||
path: syn::TypePath {
|
||||
qself: None,
|
||||
path: path.clone(),
|
||||
},
|
||||
params: params.to_vec(),
|
||||
}
|
||||
ty
|
||||
} else {
|
||||
TypePathType::from_type_def_path(
|
||||
ty.path(),
|
||||
@@ -256,7 +251,7 @@ impl<'a> TypeGenerator<'a> {
|
||||
},
|
||||
};
|
||||
|
||||
TypePath::Type(ty)
|
||||
TypePath::from_type(ty)
|
||||
}
|
||||
|
||||
/// Returns the derives to be applied to all generated types.
|
||||
@@ -332,6 +327,7 @@ impl Module {
|
||||
}
|
||||
}
|
||||
|
||||
/// A newtype wrapper which stores the path to the Subxt crate.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CratePath(syn::Path);
|
||||
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use crate::{api::CodegenError, CratePath};
|
||||
use std::{borrow::Cow, collections::HashMap};
|
||||
use crate::{error::TypeSubstitutionError, CratePath};
|
||||
use std::{borrow::Borrow, collections::HashMap};
|
||||
use syn::{parse_quote, spanned::Spanned as _};
|
||||
|
||||
use super::TypePath;
|
||||
use super::{TypePath, TypePathType};
|
||||
|
||||
/// A map of type substitutes. We match on the paths to generated types in order
|
||||
/// to figure out when to swap said type with some provided substitute.
|
||||
#[derive(Debug)]
|
||||
pub struct TypeSubstitutes {
|
||||
substitutes: HashMap<PathSegments, Substitute>,
|
||||
@@ -21,11 +23,12 @@ struct Substitute {
|
||||
|
||||
#[derive(Debug)]
|
||||
enum TypeParamMapping {
|
||||
None,
|
||||
Specified(Vec<u8>),
|
||||
// Pass any generics from source to target type
|
||||
PassThrough,
|
||||
// Replace any ident seen in the path with the input generic type at this index
|
||||
Specified(Vec<(syn::Ident, usize)>),
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! path_segments {
|
||||
($($ident: ident)::*) => {
|
||||
PathSegments(
|
||||
@@ -35,6 +38,7 @@ macro_rules! path_segments {
|
||||
}
|
||||
|
||||
impl TypeSubstitutes {
|
||||
/// Create a new set of type substitutes with some default substitutions in place.
|
||||
pub fn new(crate_path: &CratePath) -> Self {
|
||||
// Some hardcoded default type substitutes, can be overridden by user
|
||||
let defaults = [
|
||||
@@ -88,7 +92,7 @@ impl TypeSubstitutes {
|
||||
k,
|
||||
Substitute {
|
||||
path: v,
|
||||
param_mapping: TypeParamMapping::None,
|
||||
param_mapping: TypeParamMapping::PassThrough,
|
||||
},
|
||||
)
|
||||
})
|
||||
@@ -99,13 +103,24 @@ impl TypeSubstitutes {
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert the given substitution, overwriting any other with the same path.
|
||||
pub fn insert(
|
||||
&mut self,
|
||||
source: syn::Path,
|
||||
target: AbsolutePath,
|
||||
) -> Result<(), TypeSubstitutionError> {
|
||||
let (key, val) = TypeSubstitutes::parse_path_substitution(source, target.0)?;
|
||||
self.substitutes.insert(key, val);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Only insert the given substitution if a substitution at that path doesn't
|
||||
/// already exist.
|
||||
pub fn insert_if_not_exists(
|
||||
&mut self,
|
||||
source: syn::Path,
|
||||
target: AbsolutePath,
|
||||
) -> Result<(), CodegenError> {
|
||||
) -> Result<(), TypeSubstitutionError> {
|
||||
let (key, val) = TypeSubstitutes::parse_path_substitution(source, target.0)?;
|
||||
self.substitutes.entry(key).or_insert(val);
|
||||
Ok(())
|
||||
@@ -115,7 +130,7 @@ impl TypeSubstitutes {
|
||||
pub fn extend(
|
||||
&mut self,
|
||||
elems: impl IntoIterator<Item = (syn::Path, AbsolutePath)>,
|
||||
) -> Result<(), CodegenError> {
|
||||
) -> Result<(), TypeSubstitutionError> {
|
||||
for (source, target) in elems.into_iter() {
|
||||
let (key, val) = TypeSubstitutes::parse_path_substitution(source, target.0)?;
|
||||
self.substitutes.insert(key, val);
|
||||
@@ -127,78 +142,142 @@ impl TypeSubstitutes {
|
||||
/// source to target, and output the source => substitution mapping that we work out from this.
|
||||
fn parse_path_substitution(
|
||||
src_path: syn::Path,
|
||||
mut target_path: syn::Path,
|
||||
) -> Result<(PathSegments, Substitute), CodegenError> {
|
||||
let Some(syn::PathSegment { arguments: src_path_args, ..}) = src_path.segments.last() else {
|
||||
return Err(CodegenError::EmptySubstitutePath(src_path.span()))
|
||||
};
|
||||
let Some(syn::PathSegment { arguments: target_path_args, ..}) = target_path.segments.last_mut() else {
|
||||
return Err(CodegenError::EmptySubstitutePath(target_path.span()))
|
||||
};
|
||||
|
||||
let source_args: Vec<_> = type_args(src_path_args).collect();
|
||||
|
||||
let param_mapping = if source_args.is_empty() {
|
||||
// If the type parameters on the source type are not specified, then this means that
|
||||
// the type is either not generic or the user wants to pass through all the parameters
|
||||
TypeParamMapping::None
|
||||
} else {
|
||||
// Describe the mapping in terms of "which source param idx is used for each target param".
|
||||
// So, for each target param, find the matching source param index.
|
||||
let mapping = type_args(target_path_args)
|
||||
.filter_map(|arg| {
|
||||
source_args
|
||||
.iter()
|
||||
.position(|&src| src == arg)
|
||||
.map(|src_idx| {
|
||||
u8::try_from(src_idx).expect("type arguments to be fewer than 256; qed")
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
TypeParamMapping::Specified(mapping)
|
||||
};
|
||||
|
||||
// Now that we've parsed the type params from our target path, remove said params from
|
||||
// that path, since we're storing them separately.
|
||||
*target_path_args = syn::PathArguments::None;
|
||||
target_path: syn::Path,
|
||||
) -> Result<(PathSegments, Substitute), TypeSubstitutionError> {
|
||||
let param_mapping = Self::parse_path_param_mapping(&src_path, &target_path)?;
|
||||
|
||||
Ok((
|
||||
PathSegments::from(&src_path),
|
||||
Substitute {
|
||||
// Note; at this point, target_path might have some generics still. These
|
||||
// might be hardcoded types that we want to keep, so leave them here for now.
|
||||
path: target_path,
|
||||
param_mapping,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
/// Given a source type path, return a substituted type path if a substitution is defined.
|
||||
pub fn for_path(&self, path: impl Into<PathSegments>) -> Option<&syn::Path> {
|
||||
self.substitutes.get(&path.into()).map(|s| &s.path)
|
||||
/// Given a source and target path, parse the type params to work out the mapping from
|
||||
/// source to target, and return it.
|
||||
fn parse_path_param_mapping(
|
||||
src_path: &syn::Path,
|
||||
target_path: &syn::Path,
|
||||
) -> Result<TypeParamMapping, TypeSubstitutionError> {
|
||||
let Some(syn::PathSegment { arguments: src_path_args, ..}) = src_path.segments.last() else {
|
||||
return Err(TypeSubstitutionError::EmptySubstitutePath(src_path.span()))
|
||||
};
|
||||
let Some(syn::PathSegment { arguments: target_path_args, ..}) = target_path.segments.last() else {
|
||||
return Err(TypeSubstitutionError::EmptySubstitutePath(target_path.span()))
|
||||
};
|
||||
|
||||
// Get hold of the generic args for the "from" type, erroring if they aren't valid.
|
||||
let source_args = match src_path_args {
|
||||
syn::PathArguments::None => {
|
||||
// No generics defined on the source type:
|
||||
Vec::new()
|
||||
}
|
||||
syn::PathArguments::AngleBracketed(args) => {
|
||||
// We have generics like <A,B> defined on the source type (error for any non-ident type):
|
||||
args.args
|
||||
.iter()
|
||||
.map(|arg| match get_valid_from_substitution_type(arg) {
|
||||
Some(ident) => Ok(ident),
|
||||
None => Err(TypeSubstitutionError::InvalidFromType(arg.span())),
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?
|
||||
}
|
||||
syn::PathArguments::Parenthesized(args) => {
|
||||
// Generics like (A,B) -> defined; not allowed:
|
||||
return Err(TypeSubstitutionError::ExpectedAngleBracketGenerics(
|
||||
args.span(),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
// Get hold of the generic args for the "to" type, erroring if they aren't valid.
|
||||
let target_args = match target_path_args {
|
||||
syn::PathArguments::None => {
|
||||
// No generics on target.
|
||||
Vec::new()
|
||||
}
|
||||
syn::PathArguments::AngleBracketed(args) => {
|
||||
// We have generics like <A,B> defined on the target type.
|
||||
args.args
|
||||
.iter()
|
||||
.map(|arg| match get_valid_to_substitution_type(arg) {
|
||||
Some(arg) => Ok(arg),
|
||||
None => Err(TypeSubstitutionError::InvalidToType(arg.span())),
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?
|
||||
}
|
||||
syn::PathArguments::Parenthesized(args) => {
|
||||
// Generics like (A,B) -> defined; not allowed:
|
||||
return Err(TypeSubstitutionError::ExpectedAngleBracketGenerics(
|
||||
args.span(),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
// If no generics defined on source or target, we just apply any concrete generics
|
||||
// to the substitute type.
|
||||
if source_args.is_empty() && target_args.is_empty() {
|
||||
return Ok(TypeParamMapping::PassThrough);
|
||||
}
|
||||
|
||||
// Make a note of the idents in the source args and their indexes.
|
||||
let mapping = source_args
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(idx, ident)| (ident.clone(), idx))
|
||||
.collect();
|
||||
|
||||
Ok(TypeParamMapping::Specified(mapping))
|
||||
}
|
||||
|
||||
/// Given a source type path, return whether a substitute exists for it.
|
||||
pub fn contains(&self, path: impl Into<PathSegments>) -> bool {
|
||||
self.substitutes.contains_key(&path.into())
|
||||
}
|
||||
|
||||
/// Given a source type path and the resolved, supplied type parameters,
|
||||
/// return a new path and optionally overwritten type parameters.
|
||||
pub fn for_path_with_params<'a: 'b, 'b>(
|
||||
&'a self,
|
||||
pub fn for_path_with_params(
|
||||
&self,
|
||||
path: impl Into<PathSegments>,
|
||||
params: &'b [TypePath],
|
||||
) -> Option<(&'a syn::Path, Cow<'b, [TypePath]>)> {
|
||||
// For now, we only support:
|
||||
// 1. Reordering the generics
|
||||
// 2. Omitting certain generics
|
||||
fn reorder_params<'a>(
|
||||
params: &'a [TypePath],
|
||||
params: &[TypePath],
|
||||
) -> Option<TypePathType> {
|
||||
// If we find a substitute type, we'll take the substitute path, and
|
||||
// swap any idents with their new concrete types.
|
||||
fn replace_params(
|
||||
mut substitute_path: syn::Path,
|
||||
params: &[TypePath],
|
||||
mapping: &TypeParamMapping,
|
||||
) -> Cow<'a, [TypePath]> {
|
||||
) -> TypePathType {
|
||||
match mapping {
|
||||
TypeParamMapping::Specified(mapping) => Cow::Owned(
|
||||
mapping
|
||||
// We need to map the input params to the output params somehow:
|
||||
TypeParamMapping::Specified(mapping) => {
|
||||
// A map from ident name to replacement path.
|
||||
let replacement_map: Vec<(&syn::Ident, &TypePath)> = mapping
|
||||
.iter()
|
||||
.filter_map(|&idx| params.get(idx as usize))
|
||||
.cloned()
|
||||
.collect(),
|
||||
),
|
||||
_ => Cow::Borrowed(params),
|
||||
.filter_map(|(ident, idx)| params.get(*idx).map(|param| (ident, param)))
|
||||
.collect();
|
||||
|
||||
// Replace params in our substitute path with the incoming ones as needed.
|
||||
// No need if no replacements given.
|
||||
if !replacement_map.is_empty() {
|
||||
replace_path_params_recursively(&mut substitute_path, &replacement_map);
|
||||
}
|
||||
|
||||
TypePathType::Path {
|
||||
path: substitute_path,
|
||||
params: Vec::new(),
|
||||
}
|
||||
}
|
||||
// No mapping; just hand back the substitute path and input params.
|
||||
TypeParamMapping::PassThrough => TypePathType::Path {
|
||||
path: substitute_path,
|
||||
params: params.to_vec(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,7 +285,7 @@ impl TypeSubstitutes {
|
||||
|
||||
self.substitutes
|
||||
.get(&path)
|
||||
.map(|sub| (&sub.path, reorder_params(params, &sub.param_mapping)))
|
||||
.map(|sub| replace_params(sub.path.clone(), params, &sub.param_mapping))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,23 +313,87 @@ impl<T: scale_info::form::Form> From<&scale_info::Path<T>> for PathSegments {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an iterator over generic type parameters for `syn::PathArguments`.
|
||||
/// For example:
|
||||
/// - `<'a, T>` should only return T
|
||||
/// - `(A, B) -> String` shouldn't return anything
|
||||
fn type_args(path_args: &syn::PathArguments) -> impl Iterator<Item = &syn::Path> {
|
||||
let args_opt = match path_args {
|
||||
syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
|
||||
ref args,
|
||||
..
|
||||
}) => Some(args),
|
||||
_ => None,
|
||||
};
|
||||
/// Dig through a `syn::TypePath` (this is provided by the user in a type substitution definition as the "to" type) and
|
||||
/// swap out any type params which match the idents given in the "from" type with the corresponding concrete types.
|
||||
///
|
||||
/// eg if we have:
|
||||
///
|
||||
/// ```text
|
||||
/// from = sp_runtime::MultiAddress<A, B>,
|
||||
/// to = ::subxt::utils::Static<::sp_runtime::MultiAddress<A, B>>
|
||||
/// ```
|
||||
///
|
||||
/// And we encounter a `sp_runtime::MultiAddress<Foo, bar>`, then we will pass the `::sp_runtime::MultiAddress<A, B>`
|
||||
/// type param value into this call to turn it into `::sp_runtime::MultiAddress<Foo, Bar>`.
|
||||
fn replace_path_params_recursively<I: Borrow<syn::Ident>, P: Borrow<TypePath>>(
|
||||
path: &mut syn::Path,
|
||||
params: &Vec<(I, P)>,
|
||||
) {
|
||||
for segment in &mut path.segments {
|
||||
let syn::PathArguments::AngleBracketed(args) = &mut segment.arguments else {
|
||||
continue
|
||||
};
|
||||
for arg in &mut args.args {
|
||||
let syn::GenericArgument::Type(ty) = arg else {
|
||||
continue
|
||||
};
|
||||
let syn::Type::Path(path) = ty else {
|
||||
continue
|
||||
};
|
||||
if let Some(ident) = get_ident_from_type_path(path) {
|
||||
if let Some((_, replacement)) = params.iter().find(|(i, _)| ident == i.borrow()) {
|
||||
*ty = replacement.borrow().to_syn_type();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
replace_path_params_recursively(&mut path.path, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
args_opt.into_iter().flatten().filter_map(|arg| match arg {
|
||||
syn::GenericArgument::Type(syn::Type::Path(type_path)) => Some(&type_path.path),
|
||||
_ => None,
|
||||
})
|
||||
/// Given a "to" type in a type substitution, return the TypePath inside or None if
|
||||
/// it's not a valid "to" type.
|
||||
fn get_valid_to_substitution_type(arg: &syn::GenericArgument) -> Option<&syn::TypePath> {
|
||||
let syn::GenericArgument::Type(syn::Type::Path(type_path)) = arg else {
|
||||
// We are looking for a type, not a lifetime or anything else
|
||||
return None
|
||||
};
|
||||
Some(type_path)
|
||||
}
|
||||
|
||||
/// Given a "from" type in a type substitution, return the Ident inside or None if
|
||||
/// it's not a valid "from" type.
|
||||
fn get_valid_from_substitution_type(arg: &syn::GenericArgument) -> Option<&syn::Ident> {
|
||||
let syn::GenericArgument::Type(syn::Type::Path(type_path)) = arg else {
|
||||
// We are looking for a type, not a lifetime or anything else
|
||||
return None
|
||||
};
|
||||
get_ident_from_type_path(type_path)
|
||||
}
|
||||
|
||||
/// Given a type path, return the single ident representing it if that's all it is.
|
||||
fn get_ident_from_type_path(type_path: &syn::TypePath) -> Option<&syn::Ident> {
|
||||
if type_path.qself.is_some() {
|
||||
// No "<Foo as Bar>" type thing
|
||||
return None;
|
||||
}
|
||||
if type_path.path.leading_colon.is_some() {
|
||||
// No leading "::"
|
||||
return None;
|
||||
}
|
||||
if type_path.path.segments.len() > 1 {
|
||||
// The path should just be a single ident, not multiple
|
||||
return None;
|
||||
}
|
||||
let Some(segment) = type_path.path.segments.last() else {
|
||||
// Get the single ident (should be infallible)
|
||||
return None
|
||||
};
|
||||
if !segment.arguments.is_empty() {
|
||||
// The ident shouldn't have any of it's own generic args like A<B, C>
|
||||
return None;
|
||||
}
|
||||
Some(&segment.ident)
|
||||
}
|
||||
|
||||
/// Whether a path is absolute - starts with `::` or `crate`.
|
||||
@@ -265,14 +408,88 @@ fn is_absolute(path: &syn::Path) -> bool {
|
||||
pub struct AbsolutePath(pub syn::Path);
|
||||
|
||||
impl TryFrom<syn::Path> for AbsolutePath {
|
||||
type Error = (syn::Path, String);
|
||||
type Error = TypeSubstitutionError;
|
||||
fn try_from(value: syn::Path) -> Result<Self, Self::Error> {
|
||||
if is_absolute(&value) {
|
||||
Ok(AbsolutePath(value))
|
||||
} else {
|
||||
Err(
|
||||
(value, "The substitute path must be a global absolute path; try prefixing with `::` or `crate`".to_owned())
|
||||
)
|
||||
Err(TypeSubstitutionError::ExpectedAbsolutePath(value.span()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
macro_rules! syn_path {
|
||||
($path:path) => {{
|
||||
let path: syn::Path = syn::parse_quote!($path);
|
||||
path
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! type_path {
|
||||
($path:path) => {{
|
||||
let path: syn::Path = syn::parse_quote!($path);
|
||||
TypePath::from_syn_path(path)
|
||||
}};
|
||||
}
|
||||
|
||||
fn ident(name: &'static str) -> syn::Ident {
|
||||
syn::Ident::new(name, proc_macro2::Span::call_site())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn replacing_nested_type_params_works() {
|
||||
// Original path, replacement ident->paths, expected output path
|
||||
let paths = [
|
||||
// Works ok if nothing to replace
|
||||
(
|
||||
syn_path!(::some::path::Foo<::other::Path<A, B>>),
|
||||
vec![],
|
||||
syn_path!(::some::path::Foo<::other::Path<A, B>>),
|
||||
),
|
||||
// Simple top level replacing
|
||||
(
|
||||
syn_path!(::some::path::Foo<A>),
|
||||
vec![(ident("A"), type_path!(::new::Value))],
|
||||
syn_path!(::some::path::Foo<::new::Value>),
|
||||
),
|
||||
// More deeply nested replacing works too
|
||||
(
|
||||
syn_path!(::some::path::Foo<::other::Path<A, B>>),
|
||||
vec![(ident("A"), type_path!(::new::Value))],
|
||||
syn_path!(::some::path::Foo<::other::Path<::new::Value, B>>),
|
||||
),
|
||||
(
|
||||
syn_path!(::some::path::Foo<::other::Path<A, B>>),
|
||||
vec![
|
||||
(ident("A"), type_path!(::new::A)),
|
||||
(ident("B"), type_path!(::new::B)),
|
||||
],
|
||||
syn_path!(::some::path::Foo<::other::Path<::new::A, ::new::B>>),
|
||||
),
|
||||
(
|
||||
syn_path!(::some::path::Foo<::other::Path<A, ::more::path::to<::something::Argh<B>>>, C>),
|
||||
vec![
|
||||
(ident("A"), type_path!(::new::A)),
|
||||
(ident("B"), type_path!(::new::B)),
|
||||
],
|
||||
syn_path!(::some::path::Foo<::other::Path<::new::A, ::more::path::to<::something::Argh<::new::B>>>,C>),
|
||||
),
|
||||
// The same ident will be replaced as many times as needed:
|
||||
(
|
||||
syn_path!(::some::path::Foo<::other::Path<A, ::foo::Argh<A, B>, A>>),
|
||||
vec![(ident("A"), type_path!(::new::Value))],
|
||||
syn_path!(::some::path::Foo<::other::Path<::new::Value, ::foo::Argh<::new::Value, B>, ::new::Value>>),
|
||||
),
|
||||
];
|
||||
|
||||
for (mut path, replacements, expected) in paths {
|
||||
replace_path_params_recursively(&mut path, &replacements);
|
||||
assert_eq!(path, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
|
||||
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
|
||||
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use crate::api::CodegenError;
|
||||
use crate::error::CodegenError;
|
||||
|
||||
use super::{
|
||||
CompositeDef, CompositeDefFields, CratePath, Derives, TypeDefParameters, TypeGenerator,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
|
||||
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
|
||||
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
@@ -10,8 +10,14 @@ use scale_info::{form::PortableForm, Path, TypeDefPrimitive};
|
||||
use std::collections::BTreeSet;
|
||||
use syn::parse_quote;
|
||||
|
||||
/// An opaque struct representing a type path. The main usage of this is
|
||||
/// to spit out as tokens in some `quote!{ ... }` macro; the inner structure
|
||||
/// should be unimportant.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum TypePath {
|
||||
pub struct TypePath(TypePathInner);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum TypePathInner {
|
||||
Parameter(TypeParameter),
|
||||
Type(TypePathType),
|
||||
}
|
||||
@@ -24,15 +30,35 @@ impl quote::ToTokens for TypePath {
|
||||
}
|
||||
|
||||
impl TypePath {
|
||||
/// Construct a [`TypePath`] from a [`TypeParameter`]
|
||||
pub fn from_parameter(param: TypeParameter) -> TypePath {
|
||||
TypePath(TypePathInner::Parameter(param))
|
||||
}
|
||||
|
||||
/// Construct a [`TypePath`] from a [`TypeParameter`]
|
||||
pub fn from_type(ty: TypePathType) -> TypePath {
|
||||
TypePath(TypePathInner::Type(ty))
|
||||
}
|
||||
|
||||
/// Construct a [`TypePath`] from a [`syn::TypePath`]
|
||||
pub fn from_syn_path(path: syn::Path) -> TypePath {
|
||||
// Note; this doesn't parse the parameters or anything, but since nothing external
|
||||
// can inspect this structure, and the ToTokens impl works either way, it should be ok.
|
||||
TypePath(TypePathInner::Type(TypePathType::Path {
|
||||
path,
|
||||
params: Vec::new(),
|
||||
}))
|
||||
}
|
||||
|
||||
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(),
|
||||
match &self.0 {
|
||||
TypePathInner::Parameter(ty_param) => syn::Type::Path(parse_quote! { #ty_param }),
|
||||
TypePathInner::Type(ty) => ty.to_syn_type(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_compact(&self) -> bool {
|
||||
matches!(self, Self::Type(ty) if ty.is_compact())
|
||||
matches!(&self.0, TypePathInner::Type(ty) if ty.is_compact())
|
||||
}
|
||||
|
||||
/// Returns the type parameters in a path which are inherited from the containing type.
|
||||
@@ -45,11 +71,11 @@ impl TypePath {
|
||||
/// }
|
||||
/// ```
|
||||
pub fn parent_type_params(&self, acc: &mut BTreeSet<TypeParameter>) {
|
||||
match self {
|
||||
Self::Parameter(type_parameter) => {
|
||||
match &self.0 {
|
||||
TypePathInner::Parameter(type_parameter) => {
|
||||
acc.insert(type_parameter.clone());
|
||||
}
|
||||
Self::Type(type_path) => type_path.parent_type_params(acc),
|
||||
TypePathInner::Type(type_path) => type_path.parent_type_params(acc),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,13 +83,13 @@ impl TypePath {
|
||||
///
|
||||
/// **Note:** Utilized for transforming `std::vec::Vec<T>` into slices `&[T]` for the storage API.
|
||||
pub fn vec_type_param(&self) -> Option<&TypePath> {
|
||||
let ty = match self {
|
||||
TypePath::Type(ty) => ty,
|
||||
let ty = match &self.0 {
|
||||
TypePathInner::Type(ty) => ty,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
match ty {
|
||||
TypePathType::Vec { ref of } => Some(of),
|
||||
TypePathType::Vec { of } => Some(of),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -72,7 +98,7 @@ impl TypePath {
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum TypePathType {
|
||||
Path {
|
||||
path: syn::TypePath,
|
||||
path: syn::Path,
|
||||
params: Vec<TypePath>,
|
||||
},
|
||||
Vec {
|
||||
@@ -108,7 +134,7 @@ impl TypePathType {
|
||||
) -> Self {
|
||||
let path_segments = path.segments();
|
||||
|
||||
let path: syn::TypePath = match path_segments {
|
||||
let path: syn::Path = match path_segments {
|
||||
[] => panic!("Type has no ident"),
|
||||
[ident] => {
|
||||
// paths to prelude types
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use crate::error::FetchMetadataError;
|
||||
use jsonrpsee::{
|
||||
async_client::ClientBuilder,
|
||||
client_transport::ws::{Uri, WsTransportClientBuilder},
|
||||
@@ -67,40 +72,3 @@ async fn fetch_metadata_http(url: &Uri) -> Result<String, FetchMetadataError> {
|
||||
|
||||
Ok(client.request("state_getMetadata", rpc_params![]).await?)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum FetchMetadataError {
|
||||
DecodeError(hex::FromHexError),
|
||||
RequestError(jsonrpsee::core::Error),
|
||||
InvalidScheme(String),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for FetchMetadataError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
FetchMetadataError::DecodeError(e) => {
|
||||
write!(f, "Cannot decode hex value: {e}")
|
||||
}
|
||||
FetchMetadataError::RequestError(e) => write!(f, "Request error: {e}"),
|
||||
FetchMetadataError::InvalidScheme(s) => {
|
||||
write!(
|
||||
f,
|
||||
"'{s}' not supported, supported URI schemes are http, https, ws or wss."
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for FetchMetadataError {}
|
||||
|
||||
impl From<hex::FromHexError> for FetchMetadataError {
|
||||
fn from(e: hex::FromHexError) -> Self {
|
||||
FetchMetadataError::DecodeError(e)
|
||||
}
|
||||
}
|
||||
impl From<jsonrpsee::core::Error> for FetchMetadataError {
|
||||
fn from(e: jsonrpsee::core::Error) -> Self {
|
||||
FetchMetadataError::RequestError(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
//! Utilities to help with fetching and decoding metadata.
|
||||
|
||||
mod fetch_metadata;
|
||||
|
||||
// easy access to this type needed for fetching metadata:
|
||||
@@ -5,5 +11,5 @@ pub use jsonrpsee::client_transport::ws::Uri;
|
||||
|
||||
pub use fetch_metadata::{
|
||||
fetch_metadata_bytes, fetch_metadata_bytes_blocking, fetch_metadata_hex,
|
||||
fetch_metadata_hex_blocking, FetchMetadataError,
|
||||
fetch_metadata_hex_blocking,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user