Skip to content

Commit

Permalink
refactor(schema_derive): use a smarter algorithm for auto-generated t…
Browse files Browse the repository at this point in the history
…rait bounds (#5132)

Signed-off-by: ⭐️NINIKA⭐️ <dcnick3@users.noreply.github.com>
  • Loading branch information
DCNick3 authored Oct 10, 2024
1 parent 9a0226f commit a5794ef
Show file tree
Hide file tree
Showing 5 changed files with 297 additions and 23 deletions.
11 changes: 0 additions & 11 deletions crates/iroha_data_model/src/isi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,6 @@ mod transparent {

isi! {
/// Generic instruction to set key value at the object.
#[schema(bounds = "O: Identifiable, O::Id: IntoSchema")]
pub struct SetKeyValue<O: Identifiable> {
/// Where to set key value.
pub object: O::Id,
Expand Down Expand Up @@ -356,7 +355,6 @@ mod transparent {

isi! {
/// Generic instruction to remove key value at the object.
#[schema(bounds = "O: Identifiable, O::Id: IntoSchema")]
pub struct RemoveKeyValue<O: Identifiable> {
/// From where to remove key value.
pub object: O::Id,
Expand Down Expand Up @@ -437,7 +435,6 @@ mod transparent {

isi! {
/// Generic instruction for a registration of an object to the identifiable destination.
#[schema(bounds = "O: Registered, O::With: IntoSchema")]
#[serde(transparent)]
pub struct Register<O: Registered> {
/// The object that should be registered, should be uniquely identifiable by its id.
Expand Down Expand Up @@ -524,7 +521,6 @@ mod transparent {

isi! {
/// Generic instruction for an unregistration of an object from the identifiable destination.
#[schema(bounds = "O: Identifiable, O::Id: IntoSchema")]
pub struct Unregister<O: Identifiable> {
/// [`Identifiable::Id`] of the object which should be unregistered.
pub object: O::Id,
Expand Down Expand Up @@ -606,7 +602,6 @@ mod transparent {

isi! {
/// Generic instruction for a mint of an object to the identifiable destination.
#[schema(bounds = "O: IntoSchema, D: Identifiable, D::Id: IntoSchema")]
pub struct Mint<O, D: Identifiable> {
/// Object which should be minted.
pub object: O,
Expand Down Expand Up @@ -656,7 +651,6 @@ mod transparent {

isi! {
/// Generic instruction for a burn of an object to the identifiable destination.
#[schema(bounds = "O: IntoSchema, D: Identifiable, D::Id: IntoSchema")]
pub struct Burn<O, D: Identifiable> {
/// Object which should be burned.
pub object: O,
Expand Down Expand Up @@ -706,9 +700,6 @@ mod transparent {

isi! {
/// Generic instruction for a transfer of an object from the identifiable source to the identifiable destination.
#[schema(bounds = "S: Identifiable, S::Id: IntoSchema, \
O: IntoSchema, \
D: Identifiable, D::Id: IntoSchema")]
pub struct Transfer<S: Identifiable, O, D: Identifiable> {
/// Source object `Id`.
pub source: S::Id,
Expand Down Expand Up @@ -802,7 +793,6 @@ mod transparent {

isi! {
/// Generic instruction for granting permission to an entity.
#[schema(bounds = "O: IntoSchema, D: Identifiable, D::Id: IntoSchema")]
pub struct Grant<O, D: Identifiable> {
/// Object to grant.
pub object: O,
Expand Down Expand Up @@ -863,7 +853,6 @@ mod transparent {

isi! {
/// Generic instruction for revoking permission from an entity.
#[schema(bounds = "O: IntoSchema, D: Identifiable, D::Id: IntoSchema")]
pub struct Revoke<O, D: Identifiable> {
/// Object to revoke.
pub object: O,
Expand Down
2 changes: 1 addition & 1 deletion crates/iroha_schema_derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ proc-macro = true
[dependencies]
iroha_macro_utils = { path = "../iroha_macro_utils" }

syn = { workspace = true, features = ["default", "full"] }
syn = { workspace = true, features = ["default", "full", "visit"] }
proc-macro2 = { workspace = true }
quote = { workspace = true }
manyhow = { workspace = true, features = ["darling"] }
Expand Down
44 changes: 34 additions & 10 deletions crates/iroha_schema_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,28 @@
// darling-generated code triggers this lint
#![allow(clippy::option_if_let_else)]

mod trait_bounds;

use darling::{ast::Style, FromAttributes, FromDeriveInput, FromField, FromMeta, FromVariant};
use iroha_macro_utils::Emitter;
use manyhow::{emit, error_message, manyhow, Result};
use proc_macro2::{Span, TokenStream};
use quote::{quote, ToTokens};
use syn::parse_quote;

fn add_bounds_to_all_generic_parameters(generics: &mut syn::Generics, bound: syn::Path) {
let generic_type_parameters = generics
.type_params()
.map(|ty_param| ty_param.ident.clone())
.collect::<Vec<_>>();
if !generic_type_parameters.is_empty() {
let where_clause = generics.make_where_clause();
for ty in generic_type_parameters {
where_clause.predicates.push(parse_quote!(#ty: #bound));
}
}
}

fn override_where_clause(
emitter: &mut Emitter,
where_clause: Option<&syn::WhereClause>,
Expand All @@ -33,11 +48,9 @@ pub fn type_id_derive(input: TokenStream) -> Result<TokenStream> {
fn impl_type_id(input: &mut syn::DeriveInput) -> TokenStream {
let name = &input.ident;

input.generics.type_params_mut().for_each(|ty_param| {
ty_param
.bounds
.push(syn::parse_quote! {iroha_schema::TypeId});
});
// Unlike IntoSchema, `TypeId` bounds are required only on the generic type parameters, as in the standard "dumb" algorithm
// The schema of the fields are irrelevant here, as we only need the names of the parameters
add_bounds_to_all_generic_parameters(&mut input.generics, parse_quote!(iroha_schema::TypeId));

let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let type_id_body = trait_body(name, &input.generics, true);
Expand Down Expand Up @@ -201,11 +214,22 @@ pub fn schema_derive(input: TokenStream) -> TokenStream {
return emitter.finish_token_stream();
};

input.generics.type_params_mut().for_each(|ty_param| {
ty_param
.bounds
.push(parse_quote! {iroha_schema::IntoSchema});
});
// first of all, `IntoSchema` impls are required for all generic type parameters to be able to call `type_name` on them
add_bounds_to_all_generic_parameters(
&mut input.generics,
parse_quote!(iroha_schema::IntoSchema),
);

// add trait bounds on field types using the same algorithm that parity scale codec uses
emitter.handle(trait_bounds::add(
&input.ident,
&mut input.generics,
&input.data,
syn::parse_quote!(iroha_schema::IntoSchema),
None,
false,
&syn::parse_quote!(iroha_schema),
));

let impl_type_id = impl_type_id(&mut syn::parse2(original_input).unwrap());

Expand Down
Loading

0 comments on commit a5794ef

Please sign in to comment.