exonum_derive/
lib.rs

1// Copyright 2020 The Exonum Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//   http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! This crate provides macros for deriving some useful methods and traits for the exonum services.
16
17#![recursion_limit = "128"]
18#![deny(unsafe_code, bare_trait_objects)]
19#![warn(missing_docs, missing_debug_implementations)]
20
21extern crate proc_macro;
22
23mod db_traits;
24mod execution_fail;
25mod exonum_interface;
26mod require_artifact;
27mod service_dispatcher;
28mod service_factory;
29
30use darling::FromMeta;
31use proc_macro::TokenStream;
32use quote::ToTokens;
33use syn::{Attribute, NestedMeta};
34
35/// Derives `BinaryValue` trait. The target type must implement (de)serialization logic,
36/// which should be provided externally.
37///
38/// The trait currently supports two codecs:
39///
40/// - Protobuf serialization (used by default) via `exonum-proto` crate and its `ProtobufConvert`
41///   trait.
42/// - `bincode` serialization via the eponymous crate. Switched on by the
43///   `#[binary_value(codec = "bincode")]` attribute. Beware that `bincode` format is not as
44///   forward / backward compatible as Protobuf; hence, this codec is better suited for tests
45///   than for production code.
46///
47/// # Container Attributes
48///
49/// ## `codec`
50///
51/// Selects the serialization codec to use. Allowed values are `protobuf` (used by default)
52/// and `bincode`.
53///
54/// # Examples
55///
56/// With Protobuf serialization:
57///
58/// ```ignore
59/// #[derive(Clone, Debug, BinaryValue)]
60/// #[protobuf_convert(source = "proto::Wallet")]
61/// pub struct Wallet {
62///     /// `PublicKey` of the wallet.
63///     pub pub_key: PublicKey,
64///     /// Current balance of the wallet.
65///     pub balance: u64,
66/// }
67///
68/// let wallet = Wallet::new();
69/// let bytes = wallet.to_bytes();
70/// ```
71///
72/// With `bincode` serialization:
73///
74/// ```ignore
75/// #[derive(Clone, Debug, Serialize, Deserialize, BinaryValue)]
76/// #[binary_value(codec = "bincode")]
77/// pub struct Wallet {
78///     pub username: PublicKey,
79///     /// Current balance of the wallet.
80///     pub balance: u64,
81/// }
82///
83/// let wallet = Wallet {
84///     username: "Alice".to_owned(),
85///     balance: 100,
86/// };
87/// let bytes = wallet.to_bytes();
88/// ```
89#[proc_macro_derive(BinaryValue, attributes(binary_value))]
90pub fn binary_value(input: TokenStream) -> TokenStream {
91    db_traits::impl_binary_value(input)
92}
93
94/// Derives `ObjectHash` trait. The target type must implement `BinaryValue` trait.
95///
96/// # Example
97///
98/// ```ignore
99/// #[protobuf_convert(source = "proto::Wallet")]
100/// #[derive(Clone, Debug, ProtobufConvert, BinaryValue, ObjectHash)]
101/// pub struct Wallet {
102///     /// `PublicKey` of the wallet.
103///     pub pub_key: PublicKey,
104///     /// Current balance of the wallet.
105///     pub balance: u64,
106/// }
107///
108/// let wallet = Wallet {
109///     pub_key: KeyPair::random().public_key(),
110///     balance: 100,
111/// };
112/// let hash = wallet.object_hash();
113/// ```
114#[proc_macro_derive(ObjectHash)]
115pub fn object_hash(input: TokenStream) -> TokenStream {
116    db_traits::impl_object_hash(input)
117}
118
119/// Derives `FromAccess` trait.
120///
121/// This macro can be applied only to `struct`s, each field of which implements `FromAccess`
122/// itself (e.g., indexes, `Group`s, or `Lazy` indexes). The macro instantiates each field
123/// using the address created by appending a dot `.` and the name of the field or its override
124/// (see [below](#rename)) to the root address where the struct is created. For example,
125/// if the struct is created at the address `"foo"` and has fields `"list"` and `"map"`, they
126/// will be instantiated at addresses `"foo.list"` and `"foo.map"`, respectively.
127///
128/// The struct must have at least one type param, which will correspond to the `Access` type.
129/// The derive logic will determine this param as the first param with `T: Access` bound.
130/// If there are no such params, but there is a single type param, it will be used.
131///
132/// # Container Attributes
133///
134/// ## `transparent`
135///
136/// ```text
137/// #[from_access(transparent)]`
138/// ```
139///
140/// Switches to the *transparent* layout similarly to `#[repr(transparent)]`
141/// or `#[serde(transparent)]`.
142/// A struct with the transparent layout must have a single field. The field will be created at
143/// the same address as the struct itself (i.e., no suffix will be added).
144///
145/// # Field Attributes
146///
147/// ## `rename`
148///
149/// ```text
150/// #[from_access(rename = "name")]
151/// ```
152///
153/// Changes the suffix appended to the address when creating a field. The name should follow
154/// conventions for index names.
155#[proc_macro_derive(FromAccess, attributes(from_access))]
156pub fn from_access(input: TokenStream) -> TokenStream {
157    db_traits::impl_from_access(input)
158}
159
160/// Derives `ServiceDispatcher` trait.
161///
162/// # Container Attributes
163///
164/// ## `implements`
165///
166/// ```text
167/// #[service_dispatcher(implements("path_1", "path_2"))]
168/// ```
169///
170/// List of the interfaces which have been implemented by the service. If omitted, it's implied
171/// that the service does not implement interfaces.
172///
173/// ## `crate`
174///
175/// ```text
176/// #[service_dispatcher(crate = "path")]
177/// ```
178///
179/// Prefix of the `exonum` crate has two main values - `crate` or `exonum`. The default value
180/// is `exonum`.
181#[proc_macro_derive(ServiceDispatcher, attributes(service_dispatcher))]
182pub fn service_dispatcher(input: TokenStream) -> TokenStream {
183    service_dispatcher::impl_service_dispatcher(input)
184}
185
186/// Derives `ServiceFactory` trait.
187///
188/// # Container Attributes
189///
190/// ## `proto_sources`
191///
192/// ```text
193/// #[service_factory(proto_sources = "path")]
194/// ```
195///
196/// Path to the module that was generated by the build script, which
197/// contains the original Protobuf source files of the service. If omitted, no Protobuf sources
198/// will be included with the service artifact.
199///
200/// ## `crate`
201///
202/// ```text
203/// #[service_factory(crate = "path")]
204/// ```
205///
206/// Prefix of the `exonum` crate has two main values - `crate` or `exonum`. The default value
207/// is `exonum`.
208///
209/// ## `artifact_name`
210///
211/// ```text
212/// #[service_factory(artifact_name = "string")]
213/// ```
214///
215/// Overrides the artifact name, which is set to the crate name by default.
216///
217/// ## `artifact_version`
218///
219/// ```text
220/// #[service_factory(artifact_version = "string")]
221/// ```
222///
223/// Overrides the artifact version, which is set to the crate version by default.
224///
225/// ## `with_constructor`
226///
227/// ```text
228/// #[service_factory(with_constructor = "path")]
229/// ```
230///
231/// Overrides service constructor by a custom function with the following signature:
232///
233/// ```text
234/// fn(&ServiceFactoryImpl) -> Box<dyn Service>
235/// ```
236#[proc_macro_derive(ServiceFactory, attributes(service_factory))]
237pub fn service_factory(input: TokenStream) -> TokenStream {
238    service_factory::impl_service_factory(input)
239}
240
241/// Derives an Exonum service interface for the specified trait.
242///
243/// See the documentation of the Exonum crate for more information.
244///
245/// # Attributes
246///
247/// ## `crate`
248///
249/// ```text
250/// #[exonum_interface(crate = "path")]
251/// ```
252///
253/// Prefix of the `exonum` crate has two main values - `crate` or `exonum`. The default value
254/// is `exonum`.
255///
256/// ## `removed_method_ids`
257///
258/// ```text
259/// #[exonum_interface(removed_method_ids(0, 2, 5))]
260/// ```
261///
262/// Marks methods with the following IDs as removed. An attempt to invoke
263/// the method with corresponding ID will always result in an error.
264///
265/// Using this attribute is a recommended way to remove methods from interface, since it
266/// guarantees that method ID won't be reused.
267///
268/// This attribute cannot be used with `auto_ids` attribute set.
269///
270/// ## `id_auto_increment`
271///
272/// ```text
273/// #[exonum_interface(auto_ids)]
274/// ```
275///
276/// Enables automatic ID assignment for interface methods. This may be useful for writing tests,
277/// but not recommended for production code.
278///
279/// # Method attributes
280///
281/// ## `interface_method`
282///
283/// ```test
284/// #[interface_method(id = 0)]
285/// ```
286///
287/// All the method in the trait with `exonum_interface` attribute should have `interface_method`
288/// attribute with unsigned integer value. All the method IDs should be unique.
289#[proc_macro_attribute]
290pub fn exonum_interface(attr: TokenStream, item: TokenStream) -> TokenStream {
291    exonum_interface::impl_exonum_interface(attr, item)
292}
293
294/// Meta-information attribute for interface methods.
295///
296/// # Fields
297///
298/// ## `id` (required)
299///
300/// ```text
301/// #[interface_method(id = 0)]
302/// ```
303///
304/// Numeric identifier of the method. Should be unique for every method in the trait.
305///
306/// Using this attribute is a recommended way to remove methods from interface, since it
307/// guarantees that method ID won't be reused.
308#[proc_macro_attribute]
309pub fn interface_method(_attr: TokenStream, item: TokenStream) -> TokenStream {
310    // We don't modify the input stream, since `interface_method` attribute only
311    // provides additional metadata for `exonum_interface` attribute.
312    //
313    // This however should be a `proc_macro_attribute`, so rust compiler won't complain about
314    // unknown attribute.
315    item
316}
317
318/// Implements `ExecutionFail` trait for the given enum. Additionally,
319/// `From<MyEnum> for ExecutionError` conversion is implemented, allowing to use errors
320/// in the service code.
321///
322/// Enumeration should have an explicit discriminant for each error kind.
323/// The documentation comments for each error kind are used to derive the `Display` trait.
324///
325/// # Container Attributes
326///
327/// ## `crate`
328///
329/// ```text
330/// #[execution_fail(crate = "path")]
331/// ```
332///
333/// Prefix of the `exonum` crate has two main values - `crate` or `exonum`. The default value
334/// is `exonum`.
335///
336/// ## `kind`
337///
338/// ```text
339/// #[execution_fail(kind = "runtime")]
340/// ```
341///
342/// Error kind with the following possible values: `service`, `runtime`. The default value is
343/// `service`.
344#[proc_macro_derive(ExecutionFail, attributes(execution_fail))]
345pub fn execution_fail(input: TokenStream) -> TokenStream {
346    execution_fail::impl_execution_fail(input)
347}
348
349/// Implements `RequireArtifact` trait for the given struct or enum. The target type may
350/// be generic over type parameters.
351///
352/// # Container Attributes
353///
354/// ## `crate`
355///
356/// ```text
357/// #[require_artifact(crate = "path")]
358/// ```
359///
360/// Prefix of the `exonum` crate has two main values - `crate` or `exonum`. The default value
361/// is `exonum`.
362///
363/// ## `name`
364///
365/// ```text
366/// #[require_artifact(name = "artifact_name")]
367/// ```
368///
369/// Name of the artifact. If omitted, will be set to the name of the crate.
370///
371/// ## `version`
372///
373/// ```text
374/// #[require_artifact(version = "^1.3")]
375/// ```
376///
377/// [Semantic version requirement] on the artifact. If omitted, will be set to be semver-compatible
378/// with the current version of the crate. Depending on the use case, this may be too limiting;
379/// e.g., if a certain interface was defined in v1.0.0, `version = "^1"` may be explicitly specified
380/// in all the following crate releases.
381///
382/// [Semantic version requirement]: https://docs.rs/semver/0.9.0/semver/#requirements
383#[proc_macro_derive(RequireArtifact, attributes(require_artifact))]
384pub fn require_artifact(input: TokenStream) -> TokenStream {
385    require_artifact::impl_require_artifact(input)
386}
387
388pub(crate) fn find_meta_attrs(name: &str, args: &[Attribute]) -> Option<NestedMeta> {
389    args.as_ref()
390        .iter()
391        .filter_map(|a| a.parse_meta().ok())
392        .find(|m| m.path().is_ident(name))
393        .map(NestedMeta::from)
394}
395
396#[derive(Debug, FromMeta)]
397#[darling(default)]
398struct MainCratePath(syn::Path);
399
400impl Default for MainCratePath {
401    fn default() -> Self {
402        Self(syn::parse_str("exonum").unwrap())
403    }
404}
405
406impl ToTokens for MainCratePath {
407    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
408        self.0.to_tokens(tokens)
409    }
410}
411
412#[derive(Debug, FromMeta)]
413#[darling(default)]
414struct RustRuntimeCratePath(syn::Path);
415
416impl Default for RustRuntimeCratePath {
417    fn default() -> Self {
418        Self(syn::parse_str("exonum_rust_runtime").unwrap())
419    }
420}
421
422impl ToTokens for RustRuntimeCratePath {
423    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
424        self.0.to_tokens(tokens)
425    }
426}