protobuf_convert/
lib.rs

1// Copyright 2019 The Exonum Team, 2019 Witnet Foundation
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#![recursion_limit = "256"]
16
17extern crate proc_macro;
18
19mod pb_convert;
20
21use proc_macro::TokenStream;
22use syn::{Attribute, NestedMeta};
23
24const PB_CONVERT_ATTRIBUTE: &str = "protobuf_convert";
25const PB_SNAKE_CASE_ATTRIBUTE: &str = "snake_case";
26const DEFAULT_ONEOF_FIELD_NAME: &str = "kind";
27
28/// ProtobufConvert derive macro.
29///
30/// Attributes:
31///
32/// ## Required
33///
34/// * `#[protobuf_convert(source = "path")]`
35///
36/// ```ignore
37/// #[derive(Clone, Debug, ProtobufConvert)]
38/// #[protobuf_convert(source = "proto::Message")]
39/// pub struct Message {
40///     /// Message author id.
41///     pub author: u32,
42///     /// Message text.
43///     pub text: String,
44/// }
45///
46/// let msg = Message::new();
47/// let serialized_msg = msg.to_pb();
48///
49/// let deserialized_msg = ProtobufConvert::from_pb(serialized_msg).unwrap();
50/// assert_eq!(msg, deserialized_msg);
51/// ```
52///
53/// Corresponding proto file:
54/// ```proto
55/// message Message {
56///     // Message author id..
57///     uint32 author = 1;
58///     // Message text.
59///     string text = 2;
60/// }
61/// ```
62///
63/// This macro can also be applied to enums. In proto files enums are represented
64/// by `oneof` field. You can specify `oneof` field name, default is "kind".
65/// Corresponding proto file must contain only this oneof field. Possible enum
66/// variants are zero-field and one-field variants.
67/// Another enum attribute is `impl_from_trait`. If you specify it then `From` and `TryFrom`
68/// traits for enum variants will be generated. Note that this will not work if enum has
69/// variants with the same field types.
70/// ```ignore
71/// #[derive(Debug, Clone, ProtobufConvert)]
72/// #[protobuf_convert(source = "proto::Message", oneof_field = "message")]
73/// pub enum Message {
74///     /// Plain message.
75///     Plain(String),
76///     /// Encoded message.
77///     Encoded(String),
78/// }
79/// ```
80///
81/// Corresponding proto file:
82/// ```proto
83/// message Message {
84///     oneof message {
85///         // Plain message.
86///         string plain = 1;
87///         // Encoded message.
88///         string encoded = 2;
89///     }
90/// }
91/// ```
92///
93/// Path is the name of the corresponding protobuf generated struct.
94///
95/// * `#[protobuf_convert(source = "path", serde_pb_convert)]`
96///
97/// Implement `serde::{Serialize, Deserialize}` using structs that were generated with
98/// protobuf.
99/// For example, it should be used if you want json representation of your struct
100/// to be compatible with protobuf representation (including proper nesting of fields).
101/// For example, struct with `crypto::Hash` with this
102/// (de)serializer will be represented as
103/// ```text
104/// StructName {
105///     "hash": {
106///         "data": [1, 2, ...]
107///     },
108///     // ...
109/// }
110/// // With default (de)serializer.
111/// StructName {
112///     "hash": "12af..." // HEX
113///     // ...
114/// }
115/// ```
116#[proc_macro_derive(ProtobufConvert, attributes(protobuf_convert))]
117pub fn generate_protobuf_convert(input: TokenStream) -> TokenStream {
118    pb_convert::implement_protobuf_convert(input)
119}
120
121pub(crate) fn find_protobuf_convert_meta(args: &[Attribute]) -> Option<NestedMeta> {
122    args.as_ref()
123        .iter()
124        .filter_map(|a| a.parse_meta().ok())
125        .find(|m| m.path().is_ident(PB_CONVERT_ATTRIBUTE))
126        .map(NestedMeta::from)
127}