protobuf_codegen/customize/
mod.rs

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