-
Notifications
You must be signed in to change notification settings - Fork 261
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
Codegen for custom values in metadata #1117
Changes from 24 commits
09a045f
e2af2cb
72ad54b
8a9e76a
50da61c
e8f2140
00cbee4
f286b26
2f83d36
57be757
4a9000a
34fa447
2bb420e
4280a8e
59ac6cc
8afa2bf
c73ace1
c9e2b5b
99bf804
922fee2
e1f7889
b4c91c4
b131b60
56bdcd0
7d871d0
f348cb5
fba3902
959f24e
41513c0
175bbd5
529fd2f
a291c60
1915ae9
3a93bda
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
@@ -0,0 +1,104 @@ | ||||
// 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. | ||||
|
||||
use std::collections::HashSet; | ||||
|
||||
use crate::{types::TypeGenerator, CratePath}; | ||||
use heck::ToSnakeCase as _; | ||||
use subxt_metadata::Metadata; | ||||
|
||||
use proc_macro2::TokenStream as TokenStream2; | ||||
use quote::{format_ident, quote}; | ||||
|
||||
/// Generate the custom values mod, if there are any custom values in the metadata. Else returns None. | ||||
pub fn generate_custom_values( | ||||
metadata: &Metadata, | ||||
type_gen: &TypeGenerator, | ||||
types_mod_ident: &syn::Ident, | ||||
crate_path: &CratePath, | ||||
) -> Option<TokenStream2> { | ||||
let mut fn_names_taken = HashSet::new(); | ||||
let custom_value_fns: Vec<_> = metadata | ||||
.custom() | ||||
.iter() | ||||
.filter_map(|(key, custom)| { | ||||
generate_custom_value_fn( | ||||
key, | ||||
custom.type_id(), | ||||
type_gen, | ||||
crate_path, | ||||
&mut fn_names_taken, | ||||
) | ||||
}) | ||||
.collect(); | ||||
|
||||
(!custom_value_fns.is_empty()).then(|| { | ||||
quote!( | ||||
pub fn custom() -> custom::CustomValuesApi { | ||||
custom::CustomValuesApi | ||||
} | ||||
|
||||
pub mod custom{ | ||||
use super::#types_mod_ident; | ||||
|
||||
pub struct CustomValuesApi; | ||||
|
||||
impl CustomValuesApi { | ||||
#( #custom_value_fns )* | ||||
} | ||||
} | ||||
|
||||
|
||||
) | ||||
}); | ||||
|
||||
if custom_value_fns.is_empty() { | ||||
None | ||||
} else { | ||||
Some(quote!( | ||||
pub fn custom() -> custom::CustomValuesApi { | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With this implementation, I wonder if the metadata doesn't expose any custom values, then the users will not have the Wouldn't it be more consistent to have the Another benefit of mimicking the API would be that we don't have to collect the |
||||
custom::CustomValuesApi | ||||
} | ||||
|
||||
pub mod custom{ | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would this work without placing the Then |
||||
use super::#types_mod_ident; | ||||
|
||||
pub struct CustomValuesApi; | ||||
|
||||
impl CustomValuesApi { | ||||
#( #custom_value_fns )* | ||||
} | ||||
} | ||||
|
||||
|
||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Some extra spaces here
Suggested change
|
||||
)) | ||||
} | ||||
} | ||||
|
||||
/// Generates runtime functions for the given API metadata. | ||||
/// Returns None, if the name would not make for a valid identifier. | ||||
fn generate_custom_value_fn( | ||||
name: &str, | ||||
type_id: u32, | ||||
type_gen: &TypeGenerator, | ||||
crate_path: &CratePath, | ||||
fn_names_taken: &mut HashSet<String>, | ||||
) -> Option<TokenStream2> { | ||||
// names are transformed to snake case to make for good function identifiers. | ||||
let fn_name = name.to_snake_case(); | ||||
// Skip elements where the fn name is already occupied. E.g. if you have custom values with names "Foo" and "foo" in the metadata. | ||||
if fn_names_taken.contains(&fn_name) { | ||||
return None; | ||||
} | ||||
let fn_name_ident = format_ident!("{fn_name}"); | ||||
fn_names_taken.insert(fn_name); | ||||
|
||||
let return_ty = type_gen.resolve_type_path(type_id); | ||||
|
||||
Some(quote!( | ||||
pub fn #fn_name_ident() -> #crate_path::custom_values::StaticAddress<#return_ty> { | ||||
#crate_path::custom_values::StaticAddress::new(#name) | ||||
} | ||||
)) | ||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I guess either this or the below could be removed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with Alex that
if custom_value_fns.is_empty()
is easier for me to understand here but I always struggle to understand then/then_some on bool's