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    // Are we using codec::Encode or codec::Decode derives?
316    let are_codec_derives_used = derives.default_derives().derives().iter().any(|path| {
317        let mut segments_backwards = path.segments.iter().rev();
318        let ident = segments_backwards.next();
319        let module = segments_backwards.next();
320
321        let is_ident_match = ident.is_some_and(|s| s.ident == "Encode" || s.ident == "Decode");
322        let is_module_match = module.is_some_and(|s| s.ident == "codec");
323
324        is_ident_match && is_module_match
325    });
326
327    // If we're inserting the codec derives, we also should use `CompactAs` where necessary.
328    let compact_as_type_path =
329        are_codec_derives_used.then(|| parse_quote!(#crate_path::ext::codec::CompactAs));
330
331    TypeGeneratorSettings {
332        types_mod_ident: parse_quote!(runtime_types),
333        should_gen_docs,
334        derives,
335        substitutes,
336        decoded_bits_type_path: Some(parse_quote!(#crate_path::utils::bits::DecodedBits)),
337        compact_as_type_path,
338        compact_type_path: Some(parse_quote!(#crate_path::ext::codec::Compact)),
339        alloc_crate_path: AllocCratePath::Custom(parse_quote!(#crate_path::alloc)),
340        // Note: even when we don't use codec::Encode and codec::Decode, we need to keep #[codec(...)]
341        // attributes because `#[codec(skip)]` is still used/important with `EncodeAsType` and `DecodeAsType`.
342        insert_codec_attributes: true,
343    }
344}
345
346fn default_derives(crate_path: &syn::Path) -> DerivesRegistry {
347    let encode_crate_path = quote::quote! { #crate_path::ext::scale_encode }.to_string();
348    let decode_crate_path = quote::quote! { #crate_path::ext::scale_decode }.to_string();
349
350    let derives: [syn::Path; 3] = [
351        parse_quote!(#crate_path::ext::scale_encode::EncodeAsType),
352        parse_quote!(#crate_path::ext::scale_decode::DecodeAsType),
353        parse_quote!(Debug),
354    ];
355
356    let attributes: [syn::Attribute; 2] = [
357        parse_quote!(#[encode_as_type(crate_path = #encode_crate_path)]),
358        parse_quote!(#[decode_as_type(crate_path = #decode_crate_path)]),
359    ];
360
361    let mut derives_registry = DerivesRegistry::new();
362    derives_registry.add_derives_for_all(derives);
363    derives_registry.add_attributes_for_all(attributes);
364    derives_registry
365}
366
367fn default_substitutes(crate_path: &syn::Path) -> TypeSubstitutes {
368    let mut type_substitutes = TypeSubstitutes::new();
369
370    let defaults: [(syn::Path, syn::Path); 13] = [
371        (
372            parse_quote!(bitvec::order::Lsb0),
373            parse_quote!(#crate_path::utils::bits::Lsb0),
374        ),
375        (
376            parse_quote!(bitvec::order::Msb0),
377            parse_quote!(#crate_path::utils::bits::Msb0),
378        ),
379        (
380            parse_quote!(sp_core::crypto::AccountId32),
381            parse_quote!(#crate_path::utils::AccountId32),
382        ),
383        (
384            parse_quote!(fp_account::AccountId20),
385            parse_quote!(#crate_path::utils::AccountId20),
386        ),
387        (
388            parse_quote!(sp_runtime::multiaddress::MultiAddress),
389            parse_quote!(#crate_path::utils::MultiAddress),
390        ),
391        (
392            parse_quote!(primitive_types::H160),
393            parse_quote!(#crate_path::utils::H160),
394        ),
395        (
396            parse_quote!(primitive_types::H256),
397            parse_quote!(#crate_path::utils::H256),
398        ),
399        (
400            parse_quote!(primitive_types::H512),
401            parse_quote!(#crate_path::utils::H512),
402        ),
403        (
404            parse_quote!(frame_support::traits::misc::WrapperKeepOpaque),
405            parse_quote!(#crate_path::utils::WrapperKeepOpaque),
406        ),
407        // BTreeMap and BTreeSet impose an `Ord` constraint on their key types. This
408        // can cause an issue with generated code that doesn't impl `Ord` by default.
409        // Decoding them to Vec by default (KeyedVec is just an alias for Vec with
410        // suitable type params) avoids these issues.
411        (
412            parse_quote!(BTreeMap),
413            parse_quote!(#crate_path::utils::KeyedVec),
414        ),
415        (
416            parse_quote!(BinaryHeap),
417            parse_quote!(#crate_path::alloc::vec::Vec),
418        ),
419        (
420            parse_quote!(BTreeSet),
421            parse_quote!(#crate_path::alloc::vec::Vec),
422        ),
423        // The `UncheckedExtrinsic(pub Vec<u8>)` is part of the runtime API calls.
424        // The inner bytes represent the encoded extrinsic, however when deriving the
425        // `EncodeAsType` the bytes would be re-encoded. This leads to the bytes
426        // being altered by adding the length prefix in front of them.
427
428        // Note: Not sure if this is appropriate or not. The most recent polkadot.rs file does not have these.
429        (
430            parse_quote!(sp_runtime::generic::unchecked_extrinsic::UncheckedExtrinsic),
431            parse_quote!(#crate_path::utils::UncheckedExtrinsic),
432        ),
433    ];
434
435    let defaults = defaults.into_iter().map(|(from, to)| {
436        (
437            from,
438            absolute_path(to).expect("default substitutes above are absolute paths; qed"),
439        )
440    });
441    type_substitutes
442        .extend(defaults)
443        .expect("default substitutes can always be parsed; qed");
444    type_substitutes
445}