subxt_codegen/
lib.rs

1// Copyright 2019-2023 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    typegen::settings::substitutes::absolute_path, DerivesRegistry, TypeGeneratorSettings,
24    TypeSubstitutes, TypegenError,
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
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!("The provided crate path must be an absolute path, ie prefixed with '::' or 'crate'");
229        }
230        self.crate_path = crate_path;
231    }
232
233    /// Generate an interface, assuming that the default path to the `subxt` crate is `::subxt::ext::subxt_core`.
234    /// If the `subxt` crate is not available as a top level dependency, use `generate` and provide
235    /// a valid path to the `subxt¦ crate.
236    pub fn generate(self, metadata: Metadata) -> Result<TokenStream2, CodegenError> {
237        let crate_path = self.crate_path;
238
239        let mut derives_registry: DerivesRegistry = if self.use_default_derives {
240            default_derives(&crate_path)
241        } else {
242            DerivesRegistry::new()
243        };
244
245        derives_registry.add_derives_for_all(self.extra_global_derives);
246        derives_registry.add_attributes_for_all(self.extra_global_attributes);
247
248        for (ty, derives) in self.derives_for_type {
249            derives_registry.add_derives_for(ty, derives, false);
250        }
251        for (ty, derives) in self.derives_for_type_recursive {
252            derives_registry.add_derives_for(ty, derives, true);
253        }
254        for (ty, attributes) in self.attributes_for_type {
255            derives_registry.add_attributes_for(ty, attributes, false);
256        }
257        for (ty, attributes) in self.attributes_for_type_recursive {
258            derives_registry.add_attributes_for(ty, attributes, true);
259        }
260
261        let mut type_substitutes: TypeSubstitutes = if self.use_default_substitutions {
262            default_substitutes(&crate_path)
263        } else {
264            TypeSubstitutes::new()
265        };
266
267        for (from, with) in self.type_substitutes {
268            let abs_path = absolute_path(with).map_err(TypegenError::from)?;
269            type_substitutes
270                .insert(from, abs_path)
271                .map_err(TypegenError::from)?;
272        }
273
274        let item_mod = self.item_mod;
275        let generator = RuntimeGenerator::new(metadata);
276        let should_gen_docs = self.generate_docs;
277
278        if self.runtime_types_only {
279            generator.generate_runtime_types(
280                item_mod,
281                derives_registry,
282                type_substitutes,
283                crate_path,
284                should_gen_docs,
285            )
286        } else {
287            generator.generate_runtime(
288                item_mod,
289                derives_registry,
290                type_substitutes,
291                crate_path,
292                should_gen_docs,
293            )
294        }
295    }
296}
297
298/// The default [`scale_typegen::TypeGeneratorSettings`], subxt is using for generating code.
299/// Useful for emulating subxt's code generation settings from e.g. subxt-explorer.
300pub fn default_subxt_type_gen_settings() -> TypeGeneratorSettings {
301    let crate_path: syn::Path = parse_quote!(::subxt::ext::subxt_core);
302    let derives = default_derives(&crate_path);
303    let substitutes = default_substitutes(&crate_path);
304    subxt_type_gen_settings(derives, substitutes, &crate_path, true)
305}
306
307fn subxt_type_gen_settings(
308    derives: scale_typegen::DerivesRegistry,
309    substitutes: scale_typegen::TypeSubstitutes,
310    crate_path: &syn::Path,
311    should_gen_docs: bool,
312) -> TypeGeneratorSettings {
313    TypeGeneratorSettings {
314        types_mod_ident: parse_quote!(runtime_types),
315        should_gen_docs,
316        derives,
317        substitutes,
318        decoded_bits_type_path: Some(parse_quote!(#crate_path::utils::bits::DecodedBits)),
319        compact_as_type_path: Some(parse_quote!(#crate_path::ext::codec::CompactAs)),
320        compact_type_path: Some(parse_quote!(#crate_path::ext::codec::Compact)),
321        insert_codec_attributes: true,
322        alloc_crate_path: AllocCratePath::Custom(parse_quote!(#crate_path::alloc)),
323    }
324}
325
326fn default_derives(crate_path: &syn::Path) -> DerivesRegistry {
327    let encode_crate_path = quote::quote! { #crate_path::ext::scale_encode }.to_string();
328    let decode_crate_path = quote::quote! { #crate_path::ext::scale_decode }.to_string();
329
330    let derives: [syn::Path; 5] = [
331        parse_quote!(#crate_path::ext::scale_encode::EncodeAsType),
332        parse_quote!(#crate_path::ext::scale_decode::DecodeAsType),
333        parse_quote!(#crate_path::ext::codec::Encode),
334        parse_quote!(#crate_path::ext::codec::Decode),
335        parse_quote!(Debug),
336    ];
337
338    let attributes: [syn::Attribute; 4] = [
339        parse_quote!(#[encode_as_type(crate_path = #encode_crate_path)]),
340        parse_quote!(#[decode_as_type(crate_path = #decode_crate_path)]),
341        parse_quote!(#[codec(crate = #crate_path::ext::codec)]),
342        parse_quote!(#[codec(dumb_trait_bound)]),
343    ];
344
345    let mut derives_registry = DerivesRegistry::new();
346    derives_registry.add_derives_for_all(derives);
347    derives_registry.add_attributes_for_all(attributes);
348    derives_registry
349}
350
351fn default_substitutes(crate_path: &syn::Path) -> TypeSubstitutes {
352    let mut type_substitutes = TypeSubstitutes::new();
353
354    let defaults: [(syn::Path, syn::Path); 13] = [
355        (
356            parse_quote!(bitvec::order::Lsb0),
357            parse_quote!(#crate_path::utils::bits::Lsb0),
358        ),
359        (
360            parse_quote!(bitvec::order::Msb0),
361            parse_quote!(#crate_path::utils::bits::Msb0),
362        ),
363        (
364            parse_quote!(sp_core::crypto::AccountId32),
365            parse_quote!(#crate_path::utils::AccountId32),
366        ),
367        (
368            parse_quote!(fp_account::AccountId20),
369            parse_quote!(#crate_path::utils::AccountId20),
370        ),
371        (
372            parse_quote!(sp_runtime::multiaddress::MultiAddress),
373            parse_quote!(#crate_path::utils::MultiAddress),
374        ),
375        (
376            parse_quote!(primitive_types::H160),
377            parse_quote!(#crate_path::utils::H160),
378        ),
379        (
380            parse_quote!(primitive_types::H256),
381            parse_quote!(#crate_path::utils::H256),
382        ),
383        (
384            parse_quote!(primitive_types::H512),
385            parse_quote!(#crate_path::utils::H512),
386        ),
387        (
388            parse_quote!(frame_support::traits::misc::WrapperKeepOpaque),
389            parse_quote!(#crate_path::utils::WrapperKeepOpaque),
390        ),
391        // BTreeMap and BTreeSet impose an `Ord` constraint on their key types. This
392        // can cause an issue with generated code that doesn't impl `Ord` by default.
393        // Decoding them to Vec by default (KeyedVec is just an alias for Vec with
394        // suitable type params) avoids these issues.
395        (
396            parse_quote!(BTreeMap),
397            parse_quote!(#crate_path::utils::KeyedVec),
398        ),
399        (
400            parse_quote!(BinaryHeap),
401            parse_quote!(#crate_path::alloc::vec::Vec),
402        ),
403        (
404            parse_quote!(BTreeSet),
405            parse_quote!(#crate_path::alloc::vec::Vec),
406        ),
407        // The `UncheckedExtrinsic(pub Vec<u8>)` is part of the runtime API calls.
408        // The inner bytes represent the encoded extrinsic, however when deriving the
409        // `EncodeAsType` the bytes would be re-encoded. This leads to the bytes
410        // being altered by adding the length prefix in front of them.
411
412        // Note: Not sure if this is appropriate or not. The most recent polkadot.rs file does not have these.
413        (
414            parse_quote!(sp_runtime::generic::unchecked_extrinsic::UncheckedExtrinsic),
415            parse_quote!(#crate_path::utils::UncheckedExtrinsic),
416        ),
417    ];
418
419    let defaults = defaults.into_iter().map(|(from, to)| {
420        (
421            from,
422            absolute_path(to).expect("default substitutes above are absolute paths; qed"),
423        )
424    });
425    type_substitutes
426        .extend(defaults)
427        .expect("default substitutes can always be parsed; qed");
428    type_substitutes
429}