#![deny(missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg))]
mod api;
pub mod error;
mod ir;
#[cfg(feature = "web")]
use getrandom as _;
use api::RuntimeGenerator;
use proc_macro2::TokenStream as TokenStream2;
use scale_typegen::{
typegen::settings::{substitutes::absolute_path, AllocCratePath},
DerivesRegistry, TypeGeneratorSettings, TypeSubstitutes, TypegenError,
};
use std::collections::HashMap;
use syn::parse_quote;
pub use error::CodegenError;
pub use pezkuwi_subxt_metadata::Metadata;
pub use syn;
pub struct CodegenBuilder {
crate_path: syn::Path,
use_default_derives: bool,
use_default_substitutions: bool,
generate_docs: bool,
runtime_types_only: bool,
item_mod: syn::ItemMod,
extra_global_derives: Vec<syn::Path>,
extra_global_attributes: Vec<syn::Attribute>,
type_substitutes: HashMap<syn::Path, syn::Path>,
derives_for_type: HashMap<syn::TypePath, Vec<syn::Path>>,
attributes_for_type: HashMap<syn::TypePath, Vec<syn::Attribute>>,
derives_for_type_recursive: HashMap<syn::TypePath, Vec<syn::Path>>,
attributes_for_type_recursive: HashMap<syn::TypePath, Vec<syn::Attribute>>,
}
impl Default for CodegenBuilder {
fn default() -> Self {
CodegenBuilder {
crate_path: syn::parse_quote!(::pezkuwi_subxt::ext::pezkuwi_subxt_core),
use_default_derives: true,
use_default_substitutions: true,
generate_docs: true,
runtime_types_only: false,
item_mod: syn::parse_quote!(
pub mod api {}
),
extra_global_derives: Vec::new(),
extra_global_attributes: Vec::new(),
type_substitutes: HashMap::new(),
derives_for_type: HashMap::new(),
attributes_for_type: HashMap::new(),
derives_for_type_recursive: HashMap::new(),
attributes_for_type_recursive: HashMap::new(),
}
}
}
impl CodegenBuilder {
pub fn new() -> Self {
CodegenBuilder::default()
}
pub fn disable_default_derives(&mut self) {
self.use_default_derives = false;
}
pub fn disable_default_substitutes(&mut self) {
self.use_default_substitutions = false;
}
pub fn no_docs(&mut self) {
self.generate_docs = false;
}
pub fn runtime_types_only(&mut self) {
self.runtime_types_only = true;
}
pub fn set_additional_global_derives(&mut self, derives: Vec<syn::Path>) {
self.extra_global_derives = derives;
}
pub fn set_additional_global_attributes(&mut self, attributes: Vec<syn::Attribute>) {
self.extra_global_attributes = attributes;
}
pub fn add_derives_for_type(
&mut self,
ty: syn::TypePath,
derives: impl IntoIterator<Item = syn::Path>,
recursive: bool,
) {
if recursive {
self.derives_for_type_recursive.entry(ty).or_default().extend(derives);
} else {
self.derives_for_type.entry(ty).or_default().extend(derives);
}
}
pub fn add_attributes_for_type(
&mut self,
ty: syn::TypePath,
attributes: impl IntoIterator<Item = syn::Attribute>,
recursive: bool,
) {
if recursive {
self.attributes_for_type_recursive.entry(ty).or_default().extend(attributes);
} else {
self.attributes_for_type.entry(ty).or_default().extend(attributes);
}
}
pub fn set_type_substitute(&mut self, ty: syn::Path, with: syn::Path) {
self.type_substitutes.insert(ty, with);
}
pub fn set_target_module(&mut self, item_mod: syn::ItemMod) {
self.item_mod = item_mod;
}
pub fn set_subxt_crate_path(&mut self, crate_path: syn::Path) {
if absolute_path(crate_path.clone()).is_err() {
panic!(
"The provided crate path must be an absolute path, ie prefixed with '::' or 'crate'"
);
}
self.crate_path = crate_path;
}
pub fn generate(self, metadata: Metadata) -> Result<TokenStream2, CodegenError> {
let crate_path = self.crate_path;
let mut derives_registry: DerivesRegistry = if self.use_default_derives {
default_derives(&crate_path)
} else {
DerivesRegistry::new()
};
derives_registry.add_derives_for_all(self.extra_global_derives);
derives_registry.add_attributes_for_all(self.extra_global_attributes);
for (ty, derives) in self.derives_for_type {
derives_registry.add_derives_for(ty, derives, false);
}
for (ty, derives) in self.derives_for_type_recursive {
derives_registry.add_derives_for(ty, derives, true);
}
for (ty, attributes) in self.attributes_for_type {
derives_registry.add_attributes_for(ty, attributes, false);
}
for (ty, attributes) in self.attributes_for_type_recursive {
derives_registry.add_attributes_for(ty, attributes, true);
}
let mut type_substitutes: TypeSubstitutes = if self.use_default_substitutions {
default_substitutes(&crate_path)
} else {
TypeSubstitutes::new()
};
for (from, with) in self.type_substitutes {
let abs_path = absolute_path(with).map_err(TypegenError::from)?;
type_substitutes.insert(from, abs_path).map_err(TypegenError::from)?;
}
let item_mod = self.item_mod;
let generator = RuntimeGenerator::new(metadata);
let should_gen_docs = self.generate_docs;
if self.runtime_types_only {
generator.generate_runtime_types(
item_mod,
derives_registry,
type_substitutes,
crate_path,
should_gen_docs,
)
} else {
generator.generate_runtime(
item_mod,
derives_registry,
type_substitutes,
crate_path,
should_gen_docs,
)
}
}
}
pub fn default_subxt_type_gen_settings() -> TypeGeneratorSettings {
let crate_path: syn::Path = parse_quote!(::pezkuwi_subxt::ext::pezkuwi_subxt_core);
let derives = default_derives(&crate_path);
let substitutes = default_substitutes(&crate_path);
subxt_type_gen_settings(derives, substitutes, &crate_path, true)
}
fn subxt_type_gen_settings(
derives: scale_typegen::DerivesRegistry,
substitutes: scale_typegen::TypeSubstitutes,
crate_path: &syn::Path,
should_gen_docs: bool,
) -> TypeGeneratorSettings {
let are_codec_derives_used = derives.default_derives().derives().iter().any(|path| {
let mut segments_backwards = path.segments.iter().rev();
let ident = segments_backwards.next();
let module = segments_backwards.next();
let is_ident_match = ident.is_some_and(|s| s.ident == "Encode" || s.ident == "Decode");
let is_module_match = module.is_some_and(|s| s.ident == "codec");
is_ident_match && is_module_match
});
let compact_as_type_path =
are_codec_derives_used.then(|| parse_quote!(#crate_path::ext::codec::CompactAs));
TypeGeneratorSettings {
types_mod_ident: parse_quote!(runtime_types),
should_gen_docs,
derives,
substitutes,
decoded_bits_type_path: Some(parse_quote!(#crate_path::utils::bits::DecodedBits)),
compact_as_type_path,
compact_type_path: Some(parse_quote!(#crate_path::ext::codec::Compact)),
alloc_crate_path: AllocCratePath::Custom(parse_quote!(#crate_path::alloc)),
insert_codec_attributes: true,
}
}
fn default_derives(crate_path: &syn::Path) -> DerivesRegistry {
let encode_crate_path = quote::quote! { #crate_path::ext::scale_encode }.to_string();
let decode_crate_path = quote::quote! { #crate_path::ext::scale_decode }.to_string();
let derives: [syn::Path; 3] = [
parse_quote!(#crate_path::ext::scale_encode::EncodeAsType),
parse_quote!(#crate_path::ext::scale_decode::DecodeAsType),
parse_quote!(Debug),
];
let attributes: [syn::Attribute; 2] = [
parse_quote!(#[encode_as_type(crate_path = #encode_crate_path)]),
parse_quote!(#[decode_as_type(crate_path = #decode_crate_path)]),
];
let mut derives_registry = DerivesRegistry::new();
derives_registry.add_derives_for_all(derives);
derives_registry.add_attributes_for_all(attributes);
derives_registry
}
fn default_substitutes(crate_path: &syn::Path) -> TypeSubstitutes {
let mut type_substitutes = TypeSubstitutes::new();
let defaults: [(syn::Path, syn::Path); 13] = [
(parse_quote!(bitvec::order::Lsb0), parse_quote!(#crate_path::utils::bits::Lsb0)),
(parse_quote!(bitvec::order::Msb0), parse_quote!(#crate_path::utils::bits::Msb0)),
(
parse_quote!(pezsp_core::crypto::AccountId32),
parse_quote!(#crate_path::utils::AccountId32),
),
(parse_quote!(fp_account::AccountId20), parse_quote!(#crate_path::utils::AccountId20)),
(
parse_quote!(pezsp_runtime::multiaddress::MultiAddress),
parse_quote!(#crate_path::utils::MultiAddress),
),
(parse_quote!(primitive_types::H160), parse_quote!(#crate_path::utils::H160)),
(parse_quote!(primitive_types::H256), parse_quote!(#crate_path::utils::H256)),
(parse_quote!(primitive_types::H512), parse_quote!(#crate_path::utils::H512)),
(
parse_quote!(pezframe_support::traits::misc::WrapperKeepOpaque),
parse_quote!(#crate_path::utils::WrapperKeepOpaque),
),
(parse_quote!(BTreeMap), parse_quote!(#crate_path::utils::KeyedVec)),
(parse_quote!(BinaryHeap), parse_quote!(#crate_path::alloc::vec::Vec)),
(parse_quote!(BTreeSet), parse_quote!(#crate_path::alloc::vec::Vec)),
(
parse_quote!(pezsp_runtime::generic::unchecked_extrinsic::UncheckedExtrinsic),
parse_quote!(#crate_path::utils::UncheckedExtrinsic),
),
];
let defaults = defaults.into_iter().map(|(from, to)| {
(from, absolute_path(to).expect("default substitutes above are absolute paths; qed"))
});
type_substitutes
.extend(defaults)
.expect("default substitutes can always be parsed; qed");
type_substitutes
}