subxt_codegen/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.

//! Generate a type safe Subxt interface for a Substrate runtime from its metadata.
//! This is used by the `#[subxt]` macro and `subxt codegen` CLI command, but can also
//! be used directly if preferable.

#![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::AllocCratePath;
use scale_typegen::{
    typegen::settings::substitutes::absolute_path, DerivesRegistry, TypeGeneratorSettings,
    TypeSubstitutes, TypegenError,
};
use std::collections::HashMap;
use syn::parse_quote;

// Part of the public interface, so expose:
pub use error::CodegenError;
pub use subxt_metadata::Metadata;
pub use syn;

/// Generate a type safe interface to use with `subxt`.
/// The options exposed here are similar to those exposed via
/// the `#[subxt]` macro or via the `subxt codegen` CLI command.
/// Both use this under the hood.
///
/// # Example
///
/// Generating an interface using all of the defaults:
///
/// ```rust
/// use codec::Decode;
/// use subxt_codegen::{ Metadata, CodegenBuilder };
///
/// // Get hold of and decode some metadata:
/// let encoded = std::fs::read("../artifacts/polkadot_metadata_full.scale").unwrap();
/// let metadata = Metadata::decode(&mut &*encoded).unwrap();
///
/// // Generate a TokenStream representing the code for the interface.
/// // This can be converted to a string, displayed as-is or output from a macro.
/// let token_stream = CodegenBuilder::new().generate(metadata);
/// ````
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!(::subxt::ext::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 {
    /// Construct a builder to configure and generate a type-safe interface for Subxt.
    pub fn new() -> Self {
        CodegenBuilder::default()
    }

    /// Disable the default derives that are applied to all types.
    ///
    /// # Warning
    ///
    /// This is not recommended, and is highly likely to break some part of the
    /// generated interface. Expect compile errors.
    pub fn disable_default_derives(&mut self) {
        self.use_default_derives = false;
    }

    /// Disable the default type substitutions that are applied to the generated
    /// code.
    ///
    /// # Warning
    ///
    /// This is not recommended, and is highly likely to break some part of the
    /// generated interface. Expect compile errors.
    pub fn disable_default_substitutes(&mut self) {
        self.use_default_substitutions = false;
    }

    /// Disable the output of doc comments associated with the generated types and
    /// methods. This can reduce the generated code size at the expense of losing
    /// documentation.
    pub fn no_docs(&mut self) {
        self.generate_docs = false;
    }

    /// Only generate the types, and don't generate the rest of the Subxt specific
    /// interface.
    pub fn runtime_types_only(&mut self) {
        self.runtime_types_only = true;
    }

    /// Set the additional derives that will be applied to all types. By default,
    /// a set of derives required for Subxt are automatically added for all types.
    ///
    /// # Warning
    ///
    /// Invalid derives, or derives that cannot be applied to _all_ of the generated
    /// types (taking into account that some types are substituted for hand written ones
    /// that we cannot add extra derives for) will lead to compile errors in the
    /// generated code.
    pub fn set_additional_global_derives(&mut self, derives: Vec<syn::Path>) {
        self.extra_global_derives = derives;
    }

    /// Set the additional attributes that will be applied to all types. By default,
    /// a set of attributes required for Subxt are automatically added for all types.
    ///
    /// # Warning
    ///
    /// Invalid attributes can very easily lead to compile errors in the generated code.
    pub fn set_additional_global_attributes(&mut self, attributes: Vec<syn::Attribute>) {
        self.extra_global_attributes = attributes;
    }

    /// Set additional derives for a specific type at the path given.
    ///
    /// If you want to set the additional derives on all contained types recursively as well,
    /// you can set the `recursive` argument to `true`. If you don't do that,
    /// there might be compile errors in the generated code, if the derived trait
    /// relies on the fact that contained types also implement that trait.
    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);
        }
    }

    /// Set additional attributes for a specific type at the path given.
    ///
    /// Setting the `recursive` argument to `true` will additionally add the specified
    /// attributes to all contained types recursively.
    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);
        }
    }

    /// Substitute a type at the given path with some type at the second path. During codegen,
    /// we will avoid generating the type at the first path given, and instead point any references
    /// to that type to the second path given.
    ///
    /// The substituted type will need to implement the relevant traits to be compatible with the
    /// original, and it will need to SCALE encode and SCALE decode in a compatible way.
    pub fn set_type_substitute(&mut self, ty: syn::Path, with: syn::Path) {
        self.type_substitutes.insert(ty, with);
    }

    /// By default, all of the code is generated inside a module `pub mod api {}`. We decorate
    /// this module with a few attributes to reduce compile warnings and things. You can provide a
    /// target module here, allowing you to add additional attributes or inner code items (with the
    /// warning that duplicate identifiers will lead to compile errors).
    pub fn set_target_module(&mut self, item_mod: syn::ItemMod) {
        self.item_mod = item_mod;
    }

    /// Set the path to the `subxt` crate. By default, we expect it to be at `::subxt::ext::subxt_core`.
    ///
    /// # Panics
    ///
    /// Panics if the path provided is not an absolute path.
    pub fn set_subxt_crate_path(&mut self, crate_path: syn::Path) {
        if absolute_path(crate_path.clone()).is_err() {
            // Throw an error here, because otherwise we end up with a harder to comprehend error when
            // substitute types don't begin with an absolute path.
            panic!("The provided crate path must be an absolute path, ie prefixed with '::' or 'crate'");
        }
        self.crate_path = crate_path;
    }

    /// Generate an interface, assuming that the default path to the `subxt` crate is `::subxt::ext::subxt_core`.
    /// If the `subxt` crate is not available as a top level dependency, use `generate` and provide
    /// a valid path to the `subxt¦ crate.
    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,
            )
        }
    }
}

/// The default [`scale_typegen::TypeGeneratorSettings`], subxt is using for generating code.
/// Useful for emulating subxt's code generation settings from e.g. subxt-explorer.
pub fn default_subxt_type_gen_settings() -> TypeGeneratorSettings {
    let crate_path: syn::Path = parse_quote!(::subxt::ext::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 {
    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: Some(parse_quote!(#crate_path::ext::codec::CompactAs)),
        compact_type_path: Some(parse_quote!(#crate_path::ext::codec::Compact)),
        insert_codec_attributes: true,
        alloc_crate_path: AllocCratePath::Custom(parse_quote!(#crate_path::alloc)),
    }
}

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; 5] = [
        parse_quote!(#crate_path::ext::scale_encode::EncodeAsType),
        parse_quote!(#crate_path::ext::scale_decode::DecodeAsType),
        parse_quote!(#crate_path::ext::codec::Encode),
        parse_quote!(#crate_path::ext::codec::Decode),
        parse_quote!(Debug),
    ];

    let attributes: [syn::Attribute; 4] = [
        parse_quote!(#[encode_as_type(crate_path = #encode_crate_path)]),
        parse_quote!(#[decode_as_type(crate_path = #decode_crate_path)]),
        parse_quote!(#[codec(crate = #crate_path::ext::codec)]),
        parse_quote!(#[codec(dumb_trait_bound)]),
    ];

    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!(sp_core::crypto::AccountId32),
            parse_quote!(#crate_path::utils::AccountId32),
        ),
        (
            parse_quote!(fp_account::AccountId20),
            parse_quote!(#crate_path::utils::AccountId20),
        ),
        (
            parse_quote!(sp_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!(frame_support::traits::misc::WrapperKeepOpaque),
            parse_quote!(#crate_path::utils::WrapperKeepOpaque),
        ),
        // BTreeMap and BTreeSet impose an `Ord` constraint on their key types. This
        // can cause an issue with generated code that doesn't impl `Ord` by default.
        // Decoding them to Vec by default (KeyedVec is just an alias for Vec with
        // suitable type params) avoids these issues.
        (
            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),
        ),
        // The `UncheckedExtrinsic(pub Vec<u8>)` is part of the runtime API calls.
        // The inner bytes represent the encoded extrinsic, however when deriving the
        // `EncodeAsType` the bytes would be re-encoded. This leads to the bytes
        // being altered by adding the length prefix in front of them.

        // Note: Not sure if this is appropriate or not. The most recent polkadot.rs file does not have these.
        (
            parse_quote!(sp_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
}