Skip to content

Commit fecea84

Browse files
committed
[refactor] hyperledger-iroha#3388: Make model! a module-level macro
Signed-off-by: Daniil Polyakov <arjentix@gmail.com>
1 parent b185545 commit fecea84

35 files changed

+2823
-1387
lines changed

configs/peer/genesis.json

+1-1
Large diffs are not rendered by default.

crypto/src/hash.rs

+1-7
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,5 @@
11
#[cfg(not(feature = "std"))]
2-
use alloc::{
3-
borrow::ToOwned as _,
4-
format,
5-
string::{String, ToString as _},
6-
vec,
7-
vec::Vec,
8-
};
2+
use alloc::{borrow::ToOwned as _, format, string::String, vec, vec::Vec};
93
use core::{hash, marker::PhantomData, num::NonZeroU8, str::FromStr};
104

115
use derive_more::{DebugCustom, Deref, DerefMut, Display};

data_model/derive/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ license.workspace = true
1111
proc-macro = true
1212

1313
[dependencies]
14-
syn = "1.0.107"
14+
syn = { version = "1.0.107", features = ["full", "extra-traits"] }
1515
quote = "1.0.23"
1616
proc-macro2 = "1.0.49"
1717
proc-macro-error = "1.0.4"

data_model/derive/src/filter.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -200,14 +200,15 @@ pub fn impl_filter(event: &EventEnum) -> TokenStream {
200200
let filter_doc = format!(" Filter for {event_ident} entity");
201201

202202
quote! {
203-
iroha_data_model_derive::model! {
203+
iroha_data_model_derive::model_single! {
204204
#[derive(Debug, Clone, PartialEq, Eq, Hash, derive_more::Constructor, Decode, Encode, Deserialize, Serialize, IntoSchema)]
205205
#[doc = #filter_doc]
206206
#vis struct #filter_ident #generics {
207207
origin_filter: #fil_opt<#orig_fil<#imp_event>>,
208208
event_filter: #fil_opt<#event_filter_ident>
209209
}
210210
}
211+
211212
#[cfg(feature = "transparent_api")]
212213
impl #import_path::Filter for #filter_ident {
213214
type Event = #imp_event;
@@ -245,7 +246,7 @@ fn impl_event_filter(event: &EventEnum) -> proc_macro2::TokenStream {
245246
let event_filter_doc = format!(" Event filter for {event_ident} entity");
246247

247248
quote! {
248-
iroha_data_model_derive::model! {
249+
iroha_data_model_derive::model_single! {
249250
#[derive(Debug, Clone, PartialEq, Eq, Hash, Decode, Encode, Deserialize, Serialize, IntoSchema)]
250251
#[allow(clippy::enum_variant_names, missing_docs)]
251252
#[doc = #event_filter_doc]

data_model/derive/src/lib.rs

+35-41
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,15 @@
11
//! A crate containing various derive macros for `data_model`
22
#![allow(clippy::std_instead_of_core)]
33

4-
mod api;
54
mod filter;
65
mod has_origin;
76
mod id;
7+
mod model;
88
mod partially_tagged;
99

1010
use proc_macro::TokenStream;
1111
use syn::parse_macro_input;
1212

13-
struct Items(Vec<syn::DeriveInput>);
14-
15-
impl syn::parse::Parse for Items {
16-
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
17-
let mut items = Vec::new();
18-
19-
while !input.is_empty() {
20-
items.push(input.parse()?);
21-
}
22-
23-
Ok(Self(items))
24-
}
25-
}
26-
2713
/// Macro which controls how to export item's API. The behaviour is controlled with `transparent_api`
2814
/// feature flag. If the flag is active, item's public fields will be exposed as public, however, if
2915
/// it's not active, item will be exposed as opaque, i.e. no fields will be visible. This enables
@@ -33,12 +19,16 @@ impl syn::parse::Parse for Items {
3319
/// Additionally, this macro will rewrite private items as public when `transparent_api` is active.
3420
/// If an item should remain private regardless of consumer library, just don't wrap it in this macro.
3521
///
22+
/// Should be used only on public module named `model`.
23+
/// Macro will modify only structs, enums and unions. Other items will be left as is.
24+
///
3625
/// # Example
3726
///
3827
/// ```rust
3928
/// use iroha_data_model_derive::model;
4029
///
41-
/// model! {
30+
/// #[model]
31+
/// pub mod model {
4232
/// pub struct DataModel1 {
4333
/// pub item1: u32,
4434
/// item2: u64
@@ -51,38 +41,42 @@ impl syn::parse::Parse for Items {
5141
/// }
5242
///
5343
/// /* will produce:
54-
/// pub struct DataModel1 {
55-
/// #[cfg(feature = "transparent_api")]
56-
/// pub item1: u32,
57-
/// #[cfg(not(feature = "transparent_api"))]
58-
/// pub(crate) item1: u32,
59-
/// item2: u64
60-
/// }
44+
/// pub mod model {
45+
/// pub struct DataModel1 {
46+
/// #[cfg(feature = "transparent_api")]
47+
/// pub item1: u32,
48+
/// #[cfg(not(feature = "transparent_api"))]
49+
/// pub(crate) item1: u32,
50+
/// pub(super) item2: u64
51+
/// }
6152
///
62-
/// #[cfg(not(feature = "transparent_api"))]
63-
/// pub struct DataModel2 {
64-
/// pub item1: u32,
65-
/// item2: u64
66-
/// }
53+
/// #[cfg(not(feature = "transparent_api"))]
54+
/// pub struct DataModel2 {
55+
/// pub item1: u32,
56+
/// pub(super) item2: u64
57+
/// }
6758
///
68-
/// #[cfg(feature = "transparent_api")]
69-
/// struct DataModel2 {
70-
/// pub item1: u32,
71-
/// item2: u64
59+
/// #[cfg(feature = "transparent_api")]
60+
/// struct DataModel2 {
61+
/// pub item1: u32,
62+
/// pub(super) item2: u64
63+
/// }
7264
/// }
7365
/// */
7466
/// ```
75-
#[proc_macro]
67+
#[proc_macro_attribute]
7668
#[proc_macro_error::proc_macro_error]
77-
pub fn model(input: TokenStream) -> TokenStream {
78-
let input = parse_macro_input!(input as Items);
79-
let mut items = Vec::new();
80-
81-
for item in input.0 {
82-
items.push(api::process_item(item));
83-
}
69+
pub fn model(_attr: TokenStream, input: TokenStream) -> TokenStream {
70+
model::impl_model(&parse_macro_input!(input)).into()
71+
}
8472

85-
quote::quote! { #(#items)* }.into()
73+
/// Same as [`model`] macro, but only processes a single item.
74+
///
75+
/// You should prefer using [`model`] macro over this one.
76+
#[proc_macro]
77+
#[proc_macro_error::proc_macro_error]
78+
pub fn model_single(input: TokenStream) -> TokenStream {
79+
model::process_item(parse_macro_input!(input)).into()
8680
}
8781

8882
/// Derive macro for `Identifiable` trait which also automatically implements [`Ord`], [`Eq`],

data_model/derive/src/api.rs data_model/derive/src/model.rs

+43-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,49 @@
11
use proc_macro2::TokenStream;
2-
use quote::quote;
2+
use proc_macro_error::abort;
3+
use quote::{quote, ToTokens};
34
use syn::{parse_quote, Attribute};
45

5-
pub fn process_item(mut input: syn::DeriveInput) -> TokenStream {
6+
pub fn impl_model(input: &syn::ItemMod) -> TokenStream {
7+
let syn::ItemMod {
8+
attrs,
9+
vis,
10+
mod_token,
11+
ident,
12+
content,
13+
semi,
14+
..
15+
} = input;
16+
17+
let syn::Visibility::Public(vis_public) = vis else {
18+
abort!(input, "The `model` attribute can only be used on public modules");
19+
};
20+
if ident != "model" {
21+
abort!(
22+
input,
23+
"The `model` attribute can only be used on the `model` module"
24+
);
25+
}
26+
27+
let items_code = content.as_ref().map_or_else(Vec::new, |(_, items)| {
28+
items.iter().cloned().map(process_item).collect()
29+
});
30+
31+
quote! {
32+
#(#attrs)*
33+
#[allow(missing_docs)]
34+
#vis_public #mod_token #ident {
35+
#(#items_code)*
36+
}#semi
37+
}
38+
}
39+
40+
pub fn process_item(item: syn::Item) -> TokenStream {
41+
let mut input: syn::DeriveInput = match item {
42+
syn::Item::Struct(item_struct) => item_struct.into(),
43+
syn::Item::Enum(item_enum) => item_enum.into(),
44+
syn::Item::Union(item_union) => item_union.into(),
45+
other => return other.into_token_stream(),
46+
};
647
let vis = &input.vis;
748

849
if matches!(vis, syn::Visibility::Public(_)) {

data_model/derive/tests/ui_fail/transparent_api_private_item.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ error[E0603]: struct `QueryPayload` is private
77
note: the struct `QueryPayload` is defined here
88
--> $WORKSPACE/data_model/src/query.rs
99
|
10-
| pub(crate) struct QueryPayload {
11-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
10+
| pub use self::model::*;
11+
| ^^^^^^^^^^^^^^

data_model/src/account.rs

+54-7
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,13 @@ use std::collections::{btree_map, btree_set};
1414

1515
use derive_more::{Constructor, DebugCustom, Display};
1616
use getset::Getters;
17-
use iroha_data_model_derive::IdEqOrdHash;
17+
use iroha_data_model_derive::{model, IdEqOrdHash};
1818
use iroha_schema::IntoSchema;
1919
use parity_scale_codec::{Decode, Encode};
2020
use serde::{Deserialize, Serialize};
2121
use serde_with::{DeserializeFromStr, SerializeDisplay};
2222

23+
pub use self::model::*;
2324
use crate::{
2425
asset::{
2526
prelude::{Asset, AssetId},
@@ -28,7 +29,6 @@ use crate::{
2829
domain::prelude::*,
2930
expression::{ContainsAny, ContextValue, EvaluatesTo},
3031
metadata::Metadata,
31-
model,
3232
role::{prelude::RoleId, RoleIds},
3333
HasMetadata, Identifiable, Name, ParseError, PublicKey, Registered,
3434
};
@@ -50,7 +50,10 @@ pub const TRANSACTION_SIGNATORIES_VALUE: &str = "transaction_signatories";
5050
/// The context value name for account signatories.
5151
pub const ACCOUNT_SIGNATORIES_VALUE: &str = "account_signatories";
5252

53-
model! {
53+
#[model]
54+
pub mod model {
55+
use super::*;
56+
5457
/// Identification of an [`Account`]. Consists of Account name and Domain name.
5558
///
5659
/// # Examples
@@ -60,7 +63,23 @@ model! {
6063
///
6164
/// let id = "user@company".parse::<AccountId>().expect("Valid");
6265
/// ```
63-
#[derive(DebugCustom, Display, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Constructor, Getters, Decode, Encode, DeserializeFromStr, SerializeDisplay, IntoSchema)]
66+
#[derive(
67+
DebugCustom,
68+
Display,
69+
Clone,
70+
PartialEq,
71+
Eq,
72+
PartialOrd,
73+
Ord,
74+
Hash,
75+
Constructor,
76+
Getters,
77+
Decode,
78+
Encode,
79+
DeserializeFromStr,
80+
SerializeDisplay,
81+
IntoSchema,
82+
)]
6483
#[display(fmt = "{name}@{domain_id}")]
6584
#[debug(fmt = "{name}@{domain_id}")]
6685
#[getset(get = "pub")]
@@ -73,7 +92,18 @@ model! {
7392
}
7493

7594
/// Account entity is an authority which is used to execute `Iroha Special Instructions`.
76-
#[derive(Debug, Display, Clone, IdEqOrdHash, Getters, Decode, Encode, Deserialize, Serialize, IntoSchema)]
95+
#[derive(
96+
Debug,
97+
Display,
98+
Clone,
99+
IdEqOrdHash,
100+
Getters,
101+
Decode,
102+
Encode,
103+
Deserialize,
104+
Serialize,
105+
IntoSchema,
106+
)]
77107
#[allow(clippy::multiple_inherent_impl)]
78108
#[display(fmt = "({id})")] // TODO: Add more?
79109
#[ffi_type]
@@ -94,7 +124,9 @@ model! {
94124
}
95125

96126
/// Builder which should be submitted in a transaction to create a new [`Account`]
97-
#[derive(DebugCustom, Display, Clone, IdEqOrdHash, Decode, Encode, Deserialize, Serialize, IntoSchema)]
127+
#[derive(
128+
DebugCustom, Display, Clone, IdEqOrdHash, Decode, Encode, Deserialize, Serialize, IntoSchema,
129+
)]
98130
#[display(fmt = "[{id}]")]
99131
#[debug(fmt = "[{id:?}] {{ signatories: {signatories:?}, metadata: {metadata} }}")]
100132
#[ffi_type]
@@ -108,7 +140,22 @@ model! {
108140
}
109141

110142
/// Condition which checks if the account has the right signatures.
111-
#[derive(Debug, Display, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Constructor, Decode, Encode, Deserialize, Serialize, IntoSchema)]
143+
#[derive(
144+
Debug,
145+
Display,
146+
Clone,
147+
PartialEq,
148+
Eq,
149+
PartialOrd,
150+
Ord,
151+
Hash,
152+
Constructor,
153+
Decode,
154+
Encode,
155+
Deserialize,
156+
Serialize,
157+
IntoSchema,
158+
)]
112159
#[serde(transparent)]
113160
#[repr(transparent)]
114161
// SAFETY: `SignatureCheckCondition` has no trap representation in `EvalueatesTo<bool>`

0 commit comments

Comments
 (0)