1 pub(crate) mod ctx;
2 pub(crate) mod rustproto_proto;
3 
4 use std::fmt;
5 use std::ops::Deref;
6 use std::rc::Rc;
7 
8 use protobuf::reflect::EnumDescriptor;
9 use protobuf::reflect::FieldDescriptor;
10 use protobuf::reflect::FileDescriptor;
11 use protobuf::reflect::MessageDescriptor;
12 use protobuf::reflect::OneofDescriptor;
13 
14 /// Dynamic callback to customize code generation.
15 pub trait CustomizeCallback: 'static {
file(&self, file: &FileDescriptor) -> Customize16     fn file(&self, file: &FileDescriptor) -> Customize {
17         let _ = file;
18         Customize::default()
19     }
20 
message(&self, message: &MessageDescriptor) -> Customize21     fn message(&self, message: &MessageDescriptor) -> Customize {
22         let _ = message;
23         Customize::default()
24     }
25 
field(&self, field: &FieldDescriptor) -> Customize26     fn field(&self, field: &FieldDescriptor) -> Customize {
27         let _ = field;
28         Customize::default()
29     }
30 
special_field(&self, message: &MessageDescriptor, field: &str) -> Customize31     fn special_field(&self, message: &MessageDescriptor, field: &str) -> Customize {
32         let _ = (message, field);
33         Customize::default()
34     }
35 
enumeration(&self, enum_type: &EnumDescriptor) -> Customize36     fn enumeration(&self, enum_type: &EnumDescriptor) -> Customize {
37         let _ = enum_type;
38         Customize::default()
39     }
40 
oneof(&self, oneof: &OneofDescriptor) -> Customize41     fn oneof(&self, oneof: &OneofDescriptor) -> Customize {
42         let _ = oneof;
43         Customize::default()
44     }
45 }
46 
47 pub(crate) struct CustomizeCallbackDefault;
48 impl CustomizeCallback for CustomizeCallbackDefault {}
49 
50 #[derive(Clone)]
51 pub(crate) struct CustomizeCallbackHolder(pub(crate) Rc<dyn CustomizeCallback>);
52 
53 impl CustomizeCallbackHolder {
new(callback: impl CustomizeCallback) -> CustomizeCallbackHolder54     pub(crate) fn new(callback: impl CustomizeCallback) -> CustomizeCallbackHolder {
55         CustomizeCallbackHolder(Rc::new(callback))
56     }
57 }
58 
59 impl Deref for CustomizeCallbackHolder {
60     type Target = dyn CustomizeCallback;
61 
deref(&self) -> &Self::Target62     fn deref(&self) -> &Self::Target {
63         &*self.0
64     }
65 }
66 
67 impl Default for CustomizeCallbackHolder {
default() -> Self68     fn default() -> Self {
69         CustomizeCallbackHolder(Rc::new(CustomizeCallbackDefault))
70     }
71 }
72 
73 impl PartialEq for CustomizeCallbackHolder {
eq(&self, other: &Self) -> bool74     fn eq(&self, other: &Self) -> bool {
75         Rc::ptr_eq(&self.0, &other.0)
76     }
77 }
78 
79 impl fmt::Debug for CustomizeCallbackHolder {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result80     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
81         f.debug_struct("CustomizeCallbackWrapper")
82             .finish_non_exhaustive()
83     }
84 }
85 
86 /// Specifies style of generated code.
87 /// Generated files can be customized using this proto
88 /// or using `rustproto.proto` options.
89 #[derive(Default, Debug, Clone, PartialEq)]
90 pub struct Customize {
91     /// Code to insert before the element in the generated file.
92     pub(crate) before: Option<String>,
93     /// When false, `get_`, `set_`, `mut_` etc. accessors are not generated
94     pub(crate) generate_accessors: Option<bool>,
95     /// When false, `get_` is not generated even if `syntax = "proto2"`
96     pub(crate) generate_getter: Option<bool>,
97     /// Use `bytes::Bytes` for `bytes` fields
98     pub(crate) tokio_bytes: Option<bool>,
99     /// Use `bytes::Bytes` for `string` fields
100     pub(crate) tokio_bytes_for_string: Option<bool>,
101     /// Enable lite runtime.
102     pub(crate) lite_runtime: Option<bool>,
103     /// Generate `mod.rs` in the output directory.
104     ///
105     /// This option allows inclusion of generated files from cargo output directory.
106     ///
107     /// This option will likely be on by default in rust-protobuf version 3.
108     pub(crate) gen_mod_rs: Option<bool>,
109     /// Used internally to generate protos bundled in protobuf crate
110     /// like `descriptor.proto`
111     pub(crate) inside_protobuf: Option<bool>,
112 }
113 
114 #[derive(Debug, thiserror::Error)]
115 pub(crate) enum CustomizeParseParameterError {
116     #[error("Cannot parse bool option value: {:?}", .0)]
117     CannotParseBool(String),
118     #[error("Unknown option name: {:?}", .0)]
119     UnknownOptionName(String),
120 }
121 
122 impl Customize {
123     /// Insert code before the element in the generated file
124     /// (e. g. serde annotations, see
125     /// [example here](https://github.com/stepancheg/rust-protobuf/tree/master/protobuf-examples/customize-serde)).
before(mut self, before: &str) -> Self126     pub fn before(mut self, before: &str) -> Self {
127         self.before = Some(before.to_owned());
128         self
129     }
130 
generate_accessors(mut self, generate_accessors: bool) -> Self131     pub fn generate_accessors(mut self, generate_accessors: bool) -> Self {
132         self.generate_accessors = Some(generate_accessors);
133         self
134     }
135 
generate_getter(mut self, generate_getter: bool) -> Self136     pub fn generate_getter(mut self, generate_getter: bool) -> Self {
137         self.generate_getter = Some(generate_getter);
138         self
139     }
140 
tokio_bytes(mut self, tokio_bytes: bool) -> Self141     pub fn tokio_bytes(mut self, tokio_bytes: bool) -> Self {
142         self.tokio_bytes = Some(tokio_bytes);
143         self
144     }
145 
tokio_bytes_for_string(mut self, tokio_bytes_for_string: bool) -> Self146     pub fn tokio_bytes_for_string(mut self, tokio_bytes_for_string: bool) -> Self {
147         self.tokio_bytes_for_string = Some(tokio_bytes_for_string);
148         self
149     }
150 
151     /// Generate code for "lite runtime". Generated code contains no code for reflection.
152     /// So the generated code (and more importantly, generated binary size) is smaller,
153     /// but reflection, text format, JSON serialization won't work.
154     ///
155     /// Note when using `protoc` plugin `protoc-gen-rust`, the option name is just `lite`.
lite_runtime(mut self, lite_runtime: bool) -> Self156     pub fn lite_runtime(mut self, lite_runtime: bool) -> Self {
157         self.lite_runtime = Some(lite_runtime);
158         self
159     }
160 
161     /// Generate `mod.rs` with all the generated modules.
162     /// This option is on by default in rust-protobuf version 3.
gen_mod_rs(mut self, gen_mod_rs: bool) -> Self163     pub fn gen_mod_rs(mut self, gen_mod_rs: bool) -> Self {
164         self.gen_mod_rs = Some(gen_mod_rs);
165         self
166     }
167 
168     /// Generate code bundled in protobuf crate. Regular users don't need this option.
inside_protobuf(mut self, inside_protobuf: bool) -> Self169     pub fn inside_protobuf(mut self, inside_protobuf: bool) -> Self {
170         self.inside_protobuf = Some(inside_protobuf);
171         self
172     }
173 
174     /// Update fields of self with fields defined in other customize
update_with(&mut self, that: &Customize)175     pub fn update_with(&mut self, that: &Customize) {
176         if let Some(v) = &that.before {
177             self.before = Some(v.clone());
178         }
179         if let Some(v) = that.generate_accessors {
180             self.generate_accessors = Some(v);
181         }
182         if let Some(v) = that.generate_getter {
183             self.generate_getter = Some(v);
184         }
185         if let Some(v) = that.tokio_bytes {
186             self.tokio_bytes = Some(v);
187         }
188         if let Some(v) = that.tokio_bytes_for_string {
189             self.tokio_bytes_for_string = Some(v);
190         }
191         if let Some(v) = that.lite_runtime {
192             self.lite_runtime = Some(v);
193         }
194         if let Some(v) = that.gen_mod_rs {
195             self.gen_mod_rs = Some(v);
196         }
197         if let Some(v) = that.inside_protobuf {
198             self.inside_protobuf = Some(v);
199         }
200     }
201 
202     /// Update unset fields of self with fields from other customize
set_defaults_from(&mut self, other: &Customize)203     pub fn set_defaults_from(&mut self, other: &Customize) {
204         let mut tmp = other.clone();
205         tmp.update_with(self);
206         *self = tmp;
207     }
208 
209     /// Parse customize options from a string passed via protoc flag.
parse_from_parameter(parameter: &str) -> anyhow::Result<Customize>210     pub fn parse_from_parameter(parameter: &str) -> anyhow::Result<Customize> {
211         fn parse_bool(v: &str) -> anyhow::Result<bool> {
212             v.parse()
213                 .map_err(|_| CustomizeParseParameterError::CannotParseBool(v.to_owned()).into())
214         }
215 
216         let mut r = Customize::default();
217         for nv in parameter.split_whitespace() {
218             let (n, v) = match nv.find('=') {
219                 Some(eq) => {
220                     let n = &nv[..eq];
221                     let v = &nv[eq + 1..];
222                     (n, v)
223                 }
224                 None => (nv, "true"),
225             };
226 
227             if n == "generate_accessors" {
228                 r.generate_accessors = Some(parse_bool(v)?);
229             } else if n == "generate_getter" {
230                 r.generate_getter = Some(parse_bool(v)?);
231             } else if n == "tokio_bytes" {
232                 r.tokio_bytes = Some(parse_bool(v)?);
233             } else if n == "tokio_bytes_for_string" {
234                 r.tokio_bytes_for_string = Some(parse_bool(v)?);
235             } else if n == "lite_runtime" {
236                 r.lite_runtime = Some(parse_bool(v)?);
237             } else if n == "gen_mod_rs" {
238                 r.gen_mod_rs = Some(parse_bool(v)?);
239             } else if n == "inside_protobuf" {
240                 r.inside_protobuf = Some(parse_bool(v)?);
241             } else if n == "lite" {
242                 // Support Java and C++ protoc plugin syntax:
243                 // https://github.com/protocolbuffers/protobuf/issues/6489
244                 r.lite_runtime = Some(parse_bool(v)?);
245             } else {
246                 return Err(CustomizeParseParameterError::UnknownOptionName(n.to_owned()).into());
247             }
248         }
249         Ok(r)
250     }
251 }
252