blob: 826bd3f6dd60a022e91eec0cf862d2a325a522dc [file] [log] [blame]
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<dyn CustomizeCallback>);
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<String>,
/// When false, `get_`, `set_`, `mut_` etc. accessors are not generated
pub(crate) generate_accessors: Option<bool>,
/// When false, `get_` is not generated even if `syntax = "proto2"`
pub(crate) generate_getter: Option<bool>,
/// Use `bytes::Bytes` for `bytes` fields
pub(crate) tokio_bytes: Option<bool>,
/// Use `bytes::Bytes` for `string` fields
pub(crate) tokio_bytes_for_string: Option<bool>,
/// Enable lite runtime.
pub(crate) lite_runtime: Option<bool>,
/// 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<bool>,
/// Used internally to generate protos bundled in protobuf crate
/// like `descriptor.proto`
pub(crate) inside_protobuf: Option<bool>,
}
#[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<Customize> {
fn parse_bool(v: &str) -> anyhow::Result<bool> {
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)
}
}