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