subxt_codegen/
lib.rs

1// Copyright 2019-2025 Parity Technologies (UK) Ltd.
2// This file is dual-licensed as Apache-2.0 or GPL-3.0.
3// see LICENSE for license details.
4
5//! Generate a type safe Subxt interface for a Substrate runtime from its metadata.
6//! This is used by the `#[subxt]` macro and `subxt codegen` CLI command, but can also
7//! be used directly if preferable.
8
9#![deny(missing_docs)]
10#![cfg_attr(docsrs, feature(doc_cfg))]
11
12mod api;
13pub mod error;
14mod ir;
15
16#[cfg(feature = "web")]
17use getrandom as _;
18
19use api::RuntimeGenerator;
20use proc_macro2::TokenStream as TokenStream2;
21use scale_typegen::typegen::settings::AllocCratePath;
22use scale_typegen::{
23    DerivesRegistry, TypeGeneratorSettings, TypeSubstitutes, TypegenError,
24    typegen::settings::substitutes::absolute_path,
25};
26use std::collections::HashMap;
27use syn::parse_quote;
28
29// Part of the public interface, so expose:
30pub use error::CodegenError;
31pub use subxt_metadata::Metadata;
32pub use syn;
33
34/// Generate a type safe interface to use with `subxt`.
35/// The options exposed here are similar to those exposed via
36/// the `#[subxt]` macro or via the `subxt codegen` CLI command.
37/// Both use this under the hood.
38///
39/// # Example
40///
41/// Generating an interface using all of the defaults:
42///
43/// ```rust,standalone_crate
44/// use codec::Decode;
45/// use subxt_codegen::{ Metadata, CodegenBuilder };
46///
47/// // Get hold of and decode some metadata:
48/// let encoded = std::fs::read("../artifacts/polkadot_metadata_full.scale").unwrap();
49/// let metadata = Metadata::decode(&mut &*encoded).unwrap();
50///
51/// // Generate a TokenStream representing the code for the interface.
52/// // This can be converted to a string, displayed as-is or output from a macro.
53/// let token_stream = CodegenBuilder::new().generate(metadata);
54/// ````
55pub struct CodegenBuilder {
56    crate_path: syn::Path,
57    use_default_derives: bool,
58    use_default_substitutions: bool,
59    generate_docs: bool,
60    runtime_types_only: bool,
61    item_mod: syn::ItemMod,
62    extra_global_derives: Vec<syn::Path>,
63    extra_global_attributes: Vec<syn::Attribute>,
64    type_substitutes: HashMap<syn::Path, syn::Path>,
65    derives_for_type: HashMap<syn::TypePath, Vec<syn::Path>>,
66    attributes_for_type: HashMap<syn::TypePath, Vec<syn::Attribute>>,
67    derives_for_type_recursive: HashMap<syn::TypePath, Vec<syn::Path>>,
68    attributes_for_type_recursive: HashMap<syn::TypePath, Vec<syn::Attribute>>,
69}
70
71impl Default for CodegenBuilder {
72    fn default() -> Self {
73        CodegenBuilder {
74            crate_path: syn::parse_quote!(::subxt::ext::subxt_core),
75            use_default_derives: true,
76            use_default_substitutions: true,
77            generate_docs: true,
78            runtime_types_only: false,
79            item_mod: syn::parse_quote!(
80                pub mod api {}
81            ),
82            extra_global_derives: Vec::new(),
83            extra_global_attributes: Vec::new(),
84            type_substitutes: HashMap::new(),
85            derives_for_type: HashMap::new(),
86            attributes_for_type: HashMap::new(),
87            derives_for_type_recursive: HashMap::new(),
88            attributes_for_type_recursive: HashMap::new(),
89        }
90    }
91}
92
93impl CodegenBuilder {
94    /// Construct a builder to configure and generate a type-safe interface for Subxt.
95    pub fn new() -> Self {
96        CodegenBuilder::default()
97    }
98
99    /// Disable the default derives that are applied to all types.
100    ///
101    /// # Warning
102    ///
103    /// This is not recommended, and is highly likely to break some part of the
104    /// generated interface. Expect compile errors.
105    pub fn disable_default_derives(&mut self) {
106        self.use_default_derives = false;
107    }
108
109    /// Disable the default type substitutions that are applied to the generated
110    /// code.
111    ///
112    /// # Warning
113    ///
114    /// This is not recommended, and is highly likely to break some part of the
115    /// generated interface. Expect compile errors.
116    pub fn disable_default_substitutes(&mut self) {
117        self.use_default_substitutions = false;
118    }
119
120    /// Disable the output of doc comments associated with the generated types and
121    /// methods. This can reduce the generated code size at the expense of losing
122    /// documentation.
123    pub fn no_docs(&mut self) {
124        self.generate_docs = false;
125    }
126
127    /// Only generate the types, and don't generate the rest of the Subxt specific
128    /// interface.
129    pub fn runtime_types_only(&mut self) {
130        self.runtime_types_only = true;
131    }
132
133    /// Set the additional derives that will be applied to all types. By default,
134    /// a set of derives required for Subxt are automatically added for all types.
135    ///
136    /// # Warning
137    ///
138    /// Invalid derives, or derives that cannot be applied to _all_ of the generated
139    /// types (taking into account that some types are substituted for hand written ones
140    /// that we cannot add extra derives for) will lead to compile errors in the
141    /// generated code.
142    pub fn set_additional_global_derives(&mut self, derives: Vec<syn::Path>) {
143        self.extra_global_derives = derives;
144    }
145
146    /// Set the additional attributes that will be applied to all types. By default,
147    /// a set of attributes required for Subxt are automatically added for all types.
148    ///
149    /// # Warning
150    ///
151    /// Invalid attributes can very easily lead to compile errors in the generated code.
152    pub fn set_additional_global_attributes(&mut self, attributes: Vec<syn::Attribute>) {
153        self.extra_global_attributes = attributes;
154    }
155
156    /// Set additional derives for a specific type at the path given.
157    ///
158    /// If you want to set the additional derives on all contained types recursively as well,
159    /// you can set the `recursive` argument to `true`. If you don't do that,
160    /// there might be compile errors in the generated code, if the derived trait
161    /// relies on the fact that contained types also implement that trait.
162    pub fn add_derives_for_type(
163        &mut self,
164        ty: syn::TypePath,
165        derives: impl IntoIterator<Item = syn::Path>,
166        recursive: bool,
167    ) {
168        if recursive {
169            self.derives_for_type_recursive
170                .entry(ty)
171                .or_default()
172                .extend(derives);
173        } else {
174            self.derives_for_type.entry(ty).or_default().extend(derives);
175        }
176    }
177
178    /// Set additional attributes for a specific type at the path given.
179    ///
180    /// Setting the `recursive` argument to `true` will additionally add the specified
181    /// attributes to all contained types recursively.
182    pub fn add_attributes_for_type(
183        &mut self,
184        ty: syn::TypePath,
185        attributes: impl IntoIterator<Item = syn::Attribute>,
186        recursive: bool,
187    ) {
188        if recursive {
189            self.attributes_for_type_recursive
190                .entry(ty)
191                .or_default()
192                .extend(attributes);
193        } else {
194            self.attributes_for_type
195                .entry(ty)
196                .or_default()
197                .extend(attributes);
198        }
199    }
200
201    /// Substitute a type at the given path with some type at the second path. During codegen,
202    /// we will avoid generating the type at the first path given, and instead point any references
203    /// to that type to the second path given.
204    ///
205    /// The substituted type will need to implement the relevant traits to be compatible with the
206    /// original, and it will need to SCALE encode and SCALE decode in a compatible way.
207    pub fn set_type_substitute(&mut self, ty: syn::Path, with: syn::Path) {
208        self.type_substitutes.insert(ty, with);
209    }
210
211    /// By default, all of the code is generated inside a module `pub mod api {}`. We decorate
212    /// this module with a few attributes to reduce compile warnings and things. You can provide a
213    /// target module here, allowing you to add additional attributes or inner code items (with the
214    /// warning that duplicate identifiers will lead to compile errors).
215    pub fn set_target_module(&mut self, item_mod: syn::ItemMod) {
216        self.item_mod = item_mod;
217    }
218
219    /// Set the path to the `subxt` crate. By default, we expect it to be at `::subxt::ext::subxt_core`.
220    ///
221    /// # Panics
222    ///
223    /// Panics if the path provided is not an absolute path.
224    pub fn set_subxt_crate_path(&mut self, crate_path: syn::Path) {
225        if absolute_path(crate_path.clone()).is_err() {
226            // Throw an error here, because otherwise we end up with a harder to comprehend error when
227            // substitute types don't begin with an absolute path.
228            panic!(
229                "The provided crate path must be an absolute path, ie prefixed with '::' or 'crate'"
230            );
231        }
232        self.crate_path = crate_path;
233    }
234
235    /// Generate an interface, assuming that the default path to the `subxt` crate is `::subxt::ext::subxt_core`.
236    /// If the `subxt` crate is not available as a top level dependency, use `generate` and provide
237    /// a valid path to the `subxt¦ crate.
238    pub fn generate(self, metadata: Metadata) -> Result<TokenStream2, CodegenError> {
239        let crate_path = self.crate_path;
240
241        let mut derives_registry: DerivesRegistry = if self.use_default_derives {
242            default_derives(&crate_path)
243        } else {
244            DerivesRegistry::new()
245        };
246
247        derives_registry.add_derives_for_all(self.extra_global_derives);
248        derives_registry.add_attributes_for_all(self.extra_global_attributes);
249
250        for (ty, derives) in self.derives_for_type {
251            derives_registry.add_derives_for(ty, derives, false);
252        }
253        for (ty, derives) in self.derives_for_type_recursive {
254            derives_registry.add_derives_for(ty, derives, true);
255        }
256        for (ty, attributes) in self.attributes_for_type {
257            derives_registry.add_attributes_for(ty, attributes, false);
258        }
259        for (ty, attributes) in self.attributes_for_type_recursive {
260            derives_registry.add_attributes_for(ty, attributes, true);
261        }
262
263        let mut type_substitutes: TypeSubstitutes = if self.use_default_substitutions {
264            default_substitutes(&crate_path)
265        } else {
266            TypeSubstitutes::new()
267        };
268
269        for (from, with) in self.type_substitutes {
270            let abs_path = absolute_path(with).map_err(TypegenError::from)?;
271            type_substitutes
272                .insert(from, abs_path)
273                .map_err(TypegenError::from)?;
274        }
275
276        let item_mod = self.item_mod;
277        let generator = RuntimeGenerator::new(metadata);
278        let should_gen_docs = self.generate_docs;
279
280        if self.runtime_types_only {
281            generator.generate_runtime_types(
282                item_mod,
283                derives_registry,
284                type_substitutes,
285                crate_path,
286                should_gen_docs,
287            )
288        } else {
289            generator.generate_runtime(
290                item_mod,
291                derives_registry,
292                type_substitutes,
293                crate_path,
294                should_gen_docs,
295            )
296        }
297    }
298}
299
300/// The default [`scale_typegen::TypeGeneratorSettings`], subxt is using for generating code.
301/// Useful for emulating subxt's code generation settings from e.g. subxt-explorer.
302pub fn default_subxt_type_gen_settings() -> TypeGeneratorSettings {
303    let crate_path: syn::Path = parse_quote!(::subxt::ext::subxt_core);
304    let derives = default_derives(&crate_path);
305    let substitutes = default_substitutes(&crate_path);
306    subxt_type_gen_settings(derives, substitutes, &crate_path, true)
307}
308
309fn subxt_type_gen_settings(
310    derives: scale_typegen::DerivesRegistry,
311    substitutes: scale_typegen::TypeSubstitutes,
312    crate_path: &syn::Path,
313    should_gen_docs: bool,
314) -> TypeGeneratorSettings {
315    TypeGeneratorSettings {
316        types_mod_ident: parse_quote!(runtime_types),
317        should_gen_docs,
318        derives,
319        substitutes,
320        decoded_bits_type_path: Some(parse_quote!(#crate_path::utils::bits::DecodedBits)),
321        compact_as_type_path: Some(parse_quote!(#crate_path::ext::codec::CompactAs)),
322        compact_type_path: Some(parse_quote!(#crate_path::ext::codec::Compact)),
323        insert_codec_attributes: true,
324        alloc_crate_path: AllocCratePath::Custom(parse_quote!(#crate_path::alloc)),
325    }
326}
327
328fn default_derives(crate_path: &syn::Path) -> DerivesRegistry {
329    let encode_crate_path = quote::quote! { #crate_path::ext::scale_encode }.to_string();
330    let decode_crate_path = quote::quote! { #crate_path::ext::scale_decode }.to_string();
331
332    let derives: [syn::Path; 5] = [
333        parse_quote!(#crate_path::ext::scale_encode::EncodeAsType),
334        parse_quote!(#crate_path::ext::scale_decode::DecodeAsType),
335        parse_quote!(#crate_path::ext::codec::Encode),
336        parse_quote!(#crate_path::ext::codec::Decode),
337        parse_quote!(Debug),
338    ];
339
340    let attributes: [syn::Attribute; 4] = [
341        parse_quote!(#[encode_as_type(crate_path = #encode_crate_path)]),
342        parse_quote!(#[decode_as_type(crate_path = #decode_crate_path)]),
343        parse_quote!(#[codec(crate = #crate_path::ext::codec)]),
344        parse_quote!(#[codec(dumb_trait_bound)]),
345    ];
346
347    let mut derives_registry = DerivesRegistry::new();
348    derives_registry.add_derives_for_all(derives);
349    derives_registry.add_attributes_for_all(attributes);
350    derives_registry
351}
352
353fn default_substitutes(crate_path: &syn::Path) -> TypeSubstitutes {
354    let mut type_substitutes = TypeSubstitutes::new();
355
356    let defaults: [(syn::Path, syn::Path); 13] = [
357        (
358            parse_quote!(bitvec::order::Lsb0),
359            parse_quote!(#crate_path::utils::bits::Lsb0),
360        ),
361        (
362            parse_quote!(bitvec::order::Msb0),
363            parse_quote!(#crate_path::utils::bits::Msb0),
364        ),
365        (
366            parse_quote!(sp_core::crypto::AccountId32),
367            parse_quote!(#crate_path::utils::AccountId32),
368        ),
369        (
370            parse_quote!(fp_account::AccountId20),
371            parse_quote!(#crate_path::utils::AccountId20),
372        ),
373        (
374            parse_quote!(sp_runtime::multiaddress::MultiAddress),
375            parse_quote!(#crate_path::utils::MultiAddress),
376        ),
377        (
378            parse_quote!(primitive_types::H160),
379            parse_quote!(#crate_path::utils::H160),
380        ),
381        (
382            parse_quote!(primitive_types::H256),
383            parse_quote!(#crate_path::utils::H256),
384        ),
385        (
386            parse_quote!(primitive_types::H512),
387            parse_quote!(#crate_path::utils::H512),
388        ),
389        (
390            parse_quote!(frame_support::traits::misc::WrapperKeepOpaque),
391            parse_quote!(#crate_path::utils::WrapperKeepOpaque),
392        ),
393        // BTreeMap and BTreeSet impose an `Ord` constraint on their key types. This
394        // can cause an issue with generated code that doesn't impl `Ord` by default.
395        // Decoding them to Vec by default (KeyedVec is just an alias for Vec with
396        // suitable type params) avoids these issues.
397        (
398            parse_quote!(BTreeMap),
399            parse_quote!(#crate_path::utils::KeyedVec),
400        ),
401        (
402            parse_quote!(BinaryHeap),
403            parse_quote!(#crate_path::alloc::vec::Vec),
404        ),
405        (
406            parse_quote!(BTreeSet),
407            parse_quote!(#crate_path::alloc::vec::Vec),
408        ),
409        // The `UncheckedExtrinsic(pub Vec<u8>)` is part of the runtime API calls.
410        // The inner bytes represent the encoded extrinsic, however when deriving the
411        // `EncodeAsType` the bytes would be re-encoded. This leads to the bytes
412        // being altered by adding the length prefix in front of them.
413
414        // Note: Not sure if this is appropriate or not. The most recent polkadot.rs file does not have these.
415        (
416            parse_quote!(sp_runtime::generic::unchecked_extrinsic::UncheckedExtrinsic),
417            parse_quote!(#crate_path::utils::UncheckedExtrinsic),
418        ),
419    ];
420
421    let defaults = defaults.into_iter().map(|(from, to)| {
422        (
423            from,
424            absolute_path(to).expect("default substitutes above are absolute paths; qed"),
425        )
426    });
427    type_substitutes
428        .extend(defaults)
429        .expect("default substitutes can always be parsed; qed");
430    type_substitutes
431}