subxt_codegen/api/
constants.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
5use heck::ToSnakeCase as _;
6use proc_macro2::TokenStream as TokenStream2;
7use quote::{format_ident, quote};
8use scale_typegen::TypeGenerator;
9use scale_typegen::typegen::ir::ToTokensWithSettings;
10use subxt_metadata::PalletMetadata;
11
12use super::CodegenError;
13
14/// Generate constants from the provided pallet's metadata.
15///
16/// The function creates a new module named `constants` under the pallet's module.
17/// ```rust,ignore
18/// pub mod PalletName {
19///     pub mod constants {
20///     ...
21///     }
22/// }
23/// ```
24///
25/// The constants are exposed via the `ConstantsApi` wrapper.
26///
27/// Although the constants are defined in the provided static metadata, the API
28/// ensures that the constants are returned from the runtime metadata of the node.
29/// This ensures that if the node's constants change value, we'll always see the latest values.
30///
31/// # Arguments
32///
33/// - `type_gen` - [`scale_typegen::TypeGenerator`] that contains settings and all types from the runtime metadata.
34/// - `pallet` - Pallet metadata from which the constants are generated.
35/// - `crate_path` - The crate path under which the `subxt-core` crate is located, e.g. `::subxt::ext::subxt_core` when using subxt as a dependency.
36pub fn generate_constants(
37    type_gen: &TypeGenerator,
38    pallet: &PalletMetadata,
39    crate_path: &syn::Path,
40) -> Result<TokenStream2, CodegenError> {
41    // Early return if the pallet has no constants.
42    if pallet.constants().len() == 0 {
43        return Ok(quote!());
44    }
45
46    let constant_fns = pallet
47        .constants()
48        .map(|constant| {
49            let fn_name = format_ident!("{}", constant.name().to_snake_case());
50            let pallet_name = pallet.name();
51            let constant_name = constant.name();
52            let Some(constant_hash) = pallet.constant_hash(constant_name) else {
53                return Err(CodegenError::MissingConstantMetadata(
54                    constant_name.into(),
55                    pallet_name.into(),
56                ));
57            };
58
59            let return_ty = type_gen
60                .resolve_type_path(constant.ty())?
61                .to_token_stream(type_gen.settings());
62            let docs = constant.docs();
63            let docs = type_gen
64                .settings()
65                .should_gen_docs
66                .then_some(quote! { #( #[doc = #docs ] )* })
67                .unwrap_or_default();
68
69            Ok(quote! {
70                #docs
71                pub fn #fn_name(&self) -> #crate_path::constants::address::StaticAddress<#return_ty> {
72                    #crate_path::constants::address::StaticAddress::new_static(
73                        #pallet_name,
74                        #constant_name,
75                        [#(#constant_hash,)*]
76                    )
77                }
78            })
79        })
80        .collect::<Result<Vec<_>, _>>()?;
81
82    let types_mod_ident = type_gen.types_mod_ident();
83
84    Ok(quote! {
85        pub mod constants {
86            use super::#types_mod_ident;
87
88            pub struct ConstantsApi;
89
90            impl ConstantsApi {
91                #(#constant_fns)*
92            }
93        }
94    })
95}