pub(crate) mod ctx; pub(crate) mod rustproto_proto; use std::fmt; use std::ops::Deref; use std::rc::Rc; use protobuf::reflect::EnumDescriptor; use protobuf::reflect::FieldDescriptor; use protobuf::reflect::FileDescriptor; use protobuf::reflect::MessageDescriptor; use protobuf::reflect::OneofDescriptor; /// Dynamic callback to customize code generation. pub trait CustomizeCallback: 'static { fn file(&self, file: &FileDescriptor) -> Customize { let _ = file; Customize::default() } fn message(&self, message: &MessageDescriptor) -> Customize { let _ = message; Customize::default() } fn field(&self, field: &FieldDescriptor) -> Customize { let _ = field; Customize::default() } fn special_field(&self, message: &MessageDescriptor, field: &str) -> Customize { let _ = (message, field); Customize::default() } fn enumeration(&self, enum_type: &EnumDescriptor) -> Customize { let _ = enum_type; Customize::default() } fn oneof(&self, oneof: &OneofDescriptor) -> Customize { let _ = oneof; Customize::default() } } pub(crate) struct CustomizeCallbackDefault; impl CustomizeCallback for CustomizeCallbackDefault {} #[derive(Clone)] pub(crate) struct CustomizeCallbackHolder(pub(crate) Rc); impl CustomizeCallbackHolder { pub(crate) fn new(callback: impl CustomizeCallback) -> CustomizeCallbackHolder { CustomizeCallbackHolder(Rc::new(callback)) } } impl Deref for CustomizeCallbackHolder { type Target = dyn CustomizeCallback; fn deref(&self) -> &Self::Target { &*self.0 } } impl Default for CustomizeCallbackHolder { fn default() -> Self { CustomizeCallbackHolder(Rc::new(CustomizeCallbackDefault)) } } impl PartialEq for CustomizeCallbackHolder { fn eq(&self, other: &Self) -> bool { Rc::ptr_eq(&self.0, &other.0) } } impl fmt::Debug for CustomizeCallbackHolder { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("CustomizeCallbackWrapper") .finish_non_exhaustive() } } /// Specifies style of generated code. /// Generated files can be customized using this proto /// or using `rustproto.proto` options. #[derive(Default, Debug, Clone, PartialEq)] pub struct Customize { /// Code to insert before the element in the generated file. pub(crate) before: Option, /// When false, `get_`, `set_`, `mut_` etc. accessors are not generated pub(crate) generate_accessors: Option, /// When false, `get_` is not generated even if `syntax = "proto2"` pub(crate) generate_getter: Option, /// Use `bytes::Bytes` for `bytes` fields pub(crate) tokio_bytes: Option, /// Use `bytes::Bytes` for `string` fields pub(crate) tokio_bytes_for_string: Option, /// Enable lite runtime. pub(crate) lite_runtime: Option, /// Generate `mod.rs` in the output directory. /// /// This option allows inclusion of generated files from cargo output directory. /// /// This option will likely be on by default in rust-protobuf version 3. pub(crate) gen_mod_rs: Option, /// Used internally to generate protos bundled in protobuf crate /// like `descriptor.proto` pub(crate) inside_protobuf: Option, } #[derive(Debug, thiserror::Error)] pub(crate) enum CustomizeParseParameterError { #[error("Cannot parse bool option value: {:?}", .0)] CannotParseBool(String), #[error("Unknown option name: {:?}", .0)] UnknownOptionName(String), } impl Customize { /// Insert code before the element in the generated file /// (e. g. serde annotations, see /// [example here](https://github.com/stepancheg/rust-protobuf/tree/master/protobuf-examples/customize-serde)). pub fn before(mut self, before: &str) -> Self { self.before = Some(before.to_owned()); self } pub fn generate_accessors(mut self, generate_accessors: bool) -> Self { self.generate_accessors = Some(generate_accessors); self } pub fn generate_getter(mut self, generate_getter: bool) -> Self { self.generate_getter = Some(generate_getter); self } pub fn tokio_bytes(mut self, tokio_bytes: bool) -> Self { self.tokio_bytes = Some(tokio_bytes); self } pub fn tokio_bytes_for_string(mut self, tokio_bytes_for_string: bool) -> Self { self.tokio_bytes_for_string = Some(tokio_bytes_for_string); self } /// Generate code for "lite runtime". Generated code contains no code for reflection. /// So the generated code (and more importantly, generated binary size) is smaller, /// but reflection, text format, JSON serialization won't work. /// /// Note when using `protoc` plugin `protoc-gen-rust`, the option name is just `lite`. pub fn lite_runtime(mut self, lite_runtime: bool) -> Self { self.lite_runtime = Some(lite_runtime); self } /// Generate `mod.rs` with all the generated modules. /// This option is on by default in rust-protobuf version 3. pub fn gen_mod_rs(mut self, gen_mod_rs: bool) -> Self { self.gen_mod_rs = Some(gen_mod_rs); self } /// Generate code bundled in protobuf crate. Regular users don't need this option. pub fn inside_protobuf(mut self, inside_protobuf: bool) -> Self { self.inside_protobuf = Some(inside_protobuf); self } /// Update fields of self with fields defined in other customize pub fn update_with(&mut self, that: &Customize) { if let Some(v) = &that.before { self.before = Some(v.clone()); } if let Some(v) = that.generate_accessors { self.generate_accessors = Some(v); } if let Some(v) = that.generate_getter { self.generate_getter = Some(v); } if let Some(v) = that.tokio_bytes { self.tokio_bytes = Some(v); } if let Some(v) = that.tokio_bytes_for_string { self.tokio_bytes_for_string = Some(v); } if let Some(v) = that.lite_runtime { self.lite_runtime = Some(v); } if let Some(v) = that.gen_mod_rs { self.gen_mod_rs = Some(v); } if let Some(v) = that.inside_protobuf { self.inside_protobuf = Some(v); } } /// Update unset fields of self with fields from other customize pub fn set_defaults_from(&mut self, other: &Customize) { let mut tmp = other.clone(); tmp.update_with(self); *self = tmp; } /// Parse customize options from a string passed via protoc flag. pub fn parse_from_parameter(parameter: &str) -> anyhow::Result { fn parse_bool(v: &str) -> anyhow::Result { v.parse() .map_err(|_| CustomizeParseParameterError::CannotParseBool(v.to_owned()).into()) } let mut r = Customize::default(); for nv in parameter.split_whitespace() { let (n, v) = match nv.find('=') { Some(eq) => { let n = &nv[..eq]; let v = &nv[eq + 1..]; (n, v) } None => (nv, "true"), }; if n == "generate_accessors" { r.generate_accessors = Some(parse_bool(v)?); } else if n == "generate_getter" { r.generate_getter = Some(parse_bool(v)?); } else if n == "tokio_bytes" { r.tokio_bytes = Some(parse_bool(v)?); } else if n == "tokio_bytes_for_string" { r.tokio_bytes_for_string = Some(parse_bool(v)?); } else if n == "lite_runtime" { r.lite_runtime = Some(parse_bool(v)?); } else if n == "gen_mod_rs" { r.gen_mod_rs = Some(parse_bool(v)?); } else if n == "inside_protobuf" { r.inside_protobuf = Some(parse_bool(v)?); } else if n == "lite" { // Support Java and C++ protoc plugin syntax: // https://github.com/protocolbuffers/protobuf/issues/6489 r.lite_runtime = Some(parse_bool(v)?); } else { return Err(CustomizeParseParameterError::UnknownOptionName(n.to_owned()).into()); } } Ok(r) } }