Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RuntimeGenesiConfig: json macro added #5813

Merged
merged 23 commits into from
Oct 25, 2024

Conversation

michalkucharczyk
Copy link
Contributor

@michalkucharczyk michalkucharczyk commented Sep 24, 2024

This PR adds build_struct_json_patch which helps to generate a JSON used for preset.

Here is doc and example:

/// Creates a `RuntimeGenesisConfig` JSON patch, supporting recursive field initialization.
///
/// This macro creates a default `RuntimeGenesisConfig`, initializing specified fields (and nested
/// fields) with the provided values. Any fields not explicitly given are initialized with their
/// default values. The macro then serializes the fully initialized structure into a JSON blob,
/// retaining only the fields that were explicitly provided, either partially or fully initialized.
///
/// The recursive nature of this macro allows nested structures within `RuntimeGenesisConfig`
/// to be partially or fully initialized, and only the explicitly initialized fields are retained
/// in the final JSON output.
///
/// This approach prevents errors from manually creating JSON objects, such as typos or
/// inconsistencies with the `RuntimeGenesisConfig` structure, by relying on the actual
/// struct definition. This ensures the generated JSON is valid and reflects any future changes
/// to the structure.
///
/// This macro assumes that the `serde(rename_all="camelCase")` attribute is applied to
/// `RuntimeGenesisConfig`, which is typical for frame-based runtimes.
///
/// # Example
///
/// ```rust
/// use frame_support::generate_config;
/// #[derive(Default, serde::Serialize, serde::Deserialize)]
/// #[serde(rename_all = "camelCase")]
/// struct RuntimeGenesisConfig {
/// a_field: u32,
/// b_field: B,
/// c_field: u32,
/// }
///
/// #[derive(Default, serde::Serialize, serde::Deserialize)]
/// #[serde(rename_all = "camelCase")]
/// struct B {
/// i_field: u32,
/// j_field: u32,
/// }
///
/// impl B {
/// fn new() -> Self {
/// Self { i_field: 0, j_field: 2 }
/// }
/// }
///
/// assert_eq!(
/// generate_config! ( RuntimeGenesisConfig {
/// b_field: B {
/// i_field: 2,
/// }
/// }),
///
/// serde_json::json!({
/// "bField": {"iField": 2}
/// })
/// );
///
/// assert_eq!(
/// generate_config! ( RuntimeGenesisConfig {
/// a_field: 66,
///
/// }),
/// serde_json::json!({
/// "aField": 66,
/// })
/// );
///
/// assert_eq!(
/// generate_config! ( RuntimeGenesisConfig {
/// a_field: 66,
/// b_field: B::new()
///
/// }),
/// serde_json::json!({
/// "aField": 66,
/// "bField": {"iField": 0, "jField": 2}
/// })
/// );
/// ```
///
/// In this example:
/// ```ignore
/// generate_config! ( RuntimeGenesisConfig {
/// b_field: B {
/// i_field: 2,
/// }
/// }),
/// ```
/// `b_field` is partially initialized, it will be expanded to:
/// ```ignore
/// RuntimeGenesisConfig {
/// b_field {
/// i_field: 2,
/// ..Default::default()
/// },
/// ..Default::default()
/// }
/// ```
/// while all other fields are initialized with default values. The macro serializes this, retaining
/// only the provided fields.

And real-world usage:

build_struct_json_patch!(RuntimeGenesisConfig {
balances: BalancesConfig {
balances: endowed_accounts.iter().cloned().map(|k| (k, endowment)).collect(),
},
parachain_info: ParachainInfoConfig { parachain_id: id },
collator_selection: CollatorSelectionConfig {
invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(),
candidacy_bond: ASSET_HUB_ROCOCO_ED * 16,
},
session: SessionConfig {
keys: invulnerables
.into_iter()
.map(|(acc, aura)| {
(
acc.clone(), // account id
acc, // validator id
SessionKeys { aura }, // session keys
)
})
.collect(),
},
polkadot_xcm: PolkadotXcmConfig { safe_xcm_version: Some(SAFE_XCM_VERSION) },
})

Closes #5700

@michalkucharczyk michalkucharczyk requested a review from a team as a code owner September 24, 2024 09:59
@michalkucharczyk michalkucharczyk changed the title RuntimeGenesiConfig: json macro added RuntimeGenesiConfig: json macro added Sep 24, 2024
@michalkucharczyk michalkucharczyk added T1-FRAME This PR/Issue is related to core FRAME, the framework. R0-silent Changes should not be mentioned in any release notes labels Sep 24, 2024
Co-authored-by: Sebastian Kunert <skunert49@gmail.com>
@paritytech-review-bot paritytech-review-bot bot requested a review from a team October 21, 2024 19:57
@michalkucharczyk
Copy link
Contributor Author

I'll do one more renaming round.
Thinking about something like init_partial... which better reflects what macro actually does.

@skunert
Copy link
Contributor

skunert commented Oct 23, 2024

One thing I noticed while playing that is not supported is initilializing with full path, e.g. this fails:

use frame_support::build_struct_json_patch;

mod sub_module {
	#[derive(Default, serde::Serialize, serde::Deserialize)]
	#[serde(rename_all = "camelCase")]
	pub struct B {
		pub i_field: u32,
		pub j_field: u32,
	}
}
#[derive(Default, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct RuntimeGenesisConfig {
	b_field: sub_module::B,
}

build_struct_json_patch!(RuntimeGenesisConfig { b_field: sub_module::B { i_field: 10 } });

The macro parses b_field as fully initialized and throws an error about missing j_field. Not sure how easy this is to fix.

@michalkucharczyk
Copy link
Contributor Author

michalkucharczyk commented Oct 23, 2024

One thing I noticed while playing that is not supported is initilializing with full path, e.g. this fails:
...

S**t I forgot about tests for paths, should be fixed with d6c8854

Copy link
Member

@bkchr bkchr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Machine! :D

Nice!

I left some comments around macro hygiene, for the macro itself you have enough tests.

substrate/frame/support/src/generate_genesis_config.rs Outdated Show resolved Hide resolved
substrate/frame/support/src/generate_genesis_config.rs Outdated Show resolved Hide resolved
substrate/frame/support/src/generate_genesis_config.rs Outdated Show resolved Hide resolved
substrate/frame/support/src/generate_genesis_config.rs Outdated Show resolved Hide resolved
substrate/frame/support/src/generate_genesis_config.rs Outdated Show resolved Hide resolved
substrate/frame/support/src/generate_genesis_config.rs Outdated Show resolved Hide resolved
substrate/frame/support/src/generate_genesis_config.rs Outdated Show resolved Hide resolved
substrate/frame/support/src/generate_genesis_config.rs Outdated Show resolved Hide resolved
substrate/frame/support/src/generate_genesis_config.rs Outdated Show resolved Hide resolved
substrate/frame/support/src/generate_genesis_config.rs Outdated Show resolved Hide resolved
@michalkucharczyk michalkucharczyk added this pull request to the merge queue Oct 25, 2024
Merged via the queue into master with commit 7e99621 Oct 25, 2024
194 of 195 checks passed
@michalkucharczyk michalkucharczyk deleted the mku-runtime-genesis-config-macro branch October 25, 2024 09:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
R0-silent Changes should not be mentioned in any release notes T1-FRAME This PR/Issue is related to core FRAME, the framework.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Only customized fields in preset json blob?
4 participants