Skip to content

Commit

Permalink
Add prost-build::Config::disable_comments option (tokio-rs#389)
Browse files Browse the repository at this point in the history
* Add prost-build::Config::disable_comments option

* disable comments on per path basis
  • Loading branch information
danburkert authored Dec 24, 2020
1 parent 32bb509 commit a5bb7bf
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 43 deletions.
79 changes: 45 additions & 34 deletions prost-build/src/code_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ impl<'a> CodeGenerator<'a> {
}
});

self.append_doc();
self.append_doc(&fq_message_name, None);
self.append_type_attributes(&fq_message_name);
self.push_indent();
self.buf
Expand Down Expand Up @@ -264,40 +264,42 @@ impl<'a> CodeGenerator<'a> {
}
}

fn append_type_attributes(&mut self, msg_name: &str) {
assert_eq!(b'.', msg_name.as_bytes()[0]);
fn append_type_attributes(&mut self, fq_message_name: &str) {
assert_eq!(b'.', fq_message_name.as_bytes()[0]);
// TODO: this clone is dirty, but expedious.
for (matcher, attribute) in self.config.type_attributes.clone() {
if match_ident(&matcher, msg_name, None) {
if match_ident(&matcher, fq_message_name, None) {
self.push_indent();
self.buf.push_str(&attribute);
self.buf.push('\n');
}
}
}

fn append_field_attributes(&mut self, msg_name: &str, field_name: &str) {
assert_eq!(b'.', msg_name.as_bytes()[0]);
fn append_field_attributes(&mut self, fq_message_name: &str, field_name: &str) {
assert_eq!(b'.', fq_message_name.as_bytes()[0]);
// TODO: this clone is dirty, but expedious.
for (matcher, attribute) in self.config.field_attributes.clone() {
if match_ident(&matcher, msg_name, Some(field_name)) {
if match_ident(&matcher, fq_message_name, Some(field_name)) {
self.push_indent();
self.buf.push_str(&attribute);
self.buf.push('\n');
}
}
}

fn append_field(&mut self, msg_name: &str, field: FieldDescriptorProto) {
fn append_field(&mut self, fq_message_name: &str, field: FieldDescriptorProto) {
let type_ = field.r#type();
let repeated = field.label == Some(Label::Repeated as i32);
let deprecated = self.deprecated(&field);
let optional = self.optional(&field);
let ty = self.resolve_type(&field, msg_name);
let ty = self.resolve_type(&field, fq_message_name);

let boxed = !repeated
&& (type_ == Type::Message || type_ == Type::Group)
&& self.message_graph.is_nested(field.type_name(), msg_name);
&& self
.message_graph
.is_nested(field.type_name(), fq_message_name);

debug!(
" field: {:?}, type: {:?}, boxed: {}",
Expand All @@ -306,7 +308,7 @@ impl<'a> CodeGenerator<'a> {
boxed
);

self.append_doc();
self.append_doc(fq_message_name, Some(field.name()));

if deprecated {
self.push_indent();
Expand All @@ -321,7 +323,7 @@ impl<'a> CodeGenerator<'a> {
if type_ == Type::Bytes {
self.buf.push_str("=");
self.buf
.push_str(self.bytes_backing_type(&field, msg_name).as_str());
.push_str(self.bytes_backing_type(&field, fq_message_name).as_str());
}

match field.label() {
Expand Down Expand Up @@ -386,7 +388,7 @@ impl<'a> CodeGenerator<'a> {
}

self.buf.push_str("\")]\n");
self.append_field_attributes(msg_name, field.name());
self.append_field_attributes(fq_message_name, field.name());
self.push_indent();
self.buf.push_str("pub ");
self.buf.push_str(&to_snake(field.name()));
Expand All @@ -411,13 +413,13 @@ impl<'a> CodeGenerator<'a> {

fn append_map_field(
&mut self,
msg_name: &str,
fq_message_name: &str,
field: FieldDescriptorProto,
key: &FieldDescriptorProto,
value: &FieldDescriptorProto,
) {
let key_ty = self.resolve_type(key, msg_name);
let value_ty = self.resolve_type(value, msg_name);
let key_ty = self.resolve_type(key, fq_message_name);
let value_ty = self.resolve_type(value, fq_message_name);

debug!(
" map field: {:?}, key type: {:?}, value type: {:?}",
Expand All @@ -426,14 +428,14 @@ impl<'a> CodeGenerator<'a> {
value_ty
);

self.append_doc();
self.append_doc(fq_message_name, Some(field.name()));
self.push_indent();

let btree_map = self
.config
.btree_map
.iter()
.any(|matcher| match_ident(matcher, msg_name, Some(field.name())));
.any(|matcher| match_ident(matcher, fq_message_name, Some(field.name())));
let (annotation_ty, lib_name, rust_ty) = if btree_map {
("btree_map", "::prost::alloc::collections", "BTreeMap")
} else {
Expand All @@ -450,7 +452,7 @@ impl<'a> CodeGenerator<'a> {
value_tag,
field.number()
));
self.append_field_attributes(msg_name, field.name());
self.append_field_attributes(fq_message_name, field.name());
self.push_indent();
self.buf.push_str(&format!(
"pub {}: {}::{}<{}, {}>,\n",
Expand All @@ -474,7 +476,7 @@ impl<'a> CodeGenerator<'a> {
to_snake(message_name),
to_upper_camel(oneof.name())
);
self.append_doc();
self.append_doc(fq_message_name, None);
self.push_indent();
self.buf.push_str(&format!(
"#[prost(oneof=\"{}\", tags=\"{}\")]\n",
Expand All @@ -495,18 +497,18 @@ impl<'a> CodeGenerator<'a> {

fn append_oneof(
&mut self,
msg_name: &str,
fq_message_name: &str,
oneof: OneofDescriptorProto,
idx: i32,
fields: Vec<(FieldDescriptorProto, usize)>,
) {
self.path.push(8);
self.path.push(idx);
self.append_doc();
self.append_doc(fq_message_name, None);
self.path.pop();
self.path.pop();

let oneof_name = format!("{}.{}", msg_name, oneof.name());
let oneof_name = format!("{}.{}", fq_message_name, oneof.name());
self.append_type_attributes(&oneof_name);
self.push_indent();
self.buf
Expand All @@ -522,7 +524,7 @@ impl<'a> CodeGenerator<'a> {
let type_ = field.r#type();

self.path.push(idx as i32);
self.append_doc();
self.append_doc(fq_message_name, Some(field.name()));
self.path.pop();

self.push_indent();
Expand All @@ -535,10 +537,12 @@ impl<'a> CodeGenerator<'a> {
self.append_field_attributes(&oneof_name, field.name());

self.push_indent();
let ty = self.resolve_type(&field, msg_name);
let ty = self.resolve_type(&field, fq_message_name);

let boxed = (type_ == Type::Message || type_ == Type::Group)
&& self.message_graph.is_nested(field.type_name(), msg_name);
&& self
.message_graph
.is_nested(field.type_name(), fq_message_name);

debug!(
" oneof: {:?}, type: {:?}, boxed: {}",
Expand Down Expand Up @@ -575,8 +579,15 @@ impl<'a> CodeGenerator<'a> {
&self.source_info.location[idx]
}

fn append_doc(&mut self) {
Comments::from_location(self.location()).append_with_indent(self.depth, &mut self.buf);
fn append_doc(&mut self, fq_name: &str, field_name: Option<&str>) {
if !self
.config
.disable_comments
.iter()
.any(|matcher| match_ident(matcher, fq_name, field_name))
{
Comments::from_location(self.location()).append_with_indent(self.depth, &mut self.buf)
}
}

fn append_enum(&mut self, desc: EnumDescriptorProto) {
Expand All @@ -590,7 +601,7 @@ impl<'a> CodeGenerator<'a> {
return;
}

self.append_doc();
self.append_doc(&fq_enum_name, None);
self.append_type_attributes(&fq_enum_name);
self.push_indent();
self.buf.push_str(
Expand Down Expand Up @@ -636,7 +647,7 @@ impl<'a> CodeGenerator<'a> {
value: &EnumValueDescriptorProto,
prefix_to_strip: Option<String>,
) {
self.append_doc();
self.append_doc(fq_enum_name, Some(value.name()));
self.append_field_attributes(fq_enum_name, &value.name());
self.push_indent();
let name = to_upper_camel(value.name());
Expand Down Expand Up @@ -738,7 +749,7 @@ impl<'a> CodeGenerator<'a> {
self.buf.push_str("}\n");
}

fn resolve_type(&self, field: &FieldDescriptorProto, msg_name: &str) -> String {
fn resolve_type(&self, field: &FieldDescriptorProto, fq_message_name: &str) -> String {
match field.r#type() {
Type::Float => String::from("f32"),
Type::Double => String::from("f64"),
Expand All @@ -748,7 +759,7 @@ impl<'a> CodeGenerator<'a> {
Type::Int64 | Type::Sfixed64 | Type::Sint64 => String::from("i64"),
Type::Bool => String::from("bool"),
Type::String => String::from("::prost::alloc::string::String"),
Type::Bytes => match self.bytes_backing_type(field, msg_name) {
Type::Bytes => match self.bytes_backing_type(field, fq_message_name) {
BytesTy::Bytes => String::from("::prost::bytes::Bytes"),
BytesTy::Vec => String::from("::prost::alloc::vec::Vec<u8>"),
},
Expand Down Expand Up @@ -834,12 +845,12 @@ impl<'a> CodeGenerator<'a> {
}
}

fn bytes_backing_type(&self, field: &FieldDescriptorProto, msg_name: &str) -> BytesTy {
fn bytes_backing_type(&self, field: &FieldDescriptorProto, fq_message_name: &str) -> BytesTy {
let bytes = self
.config
.bytes
.iter()
.any(|matcher| match_ident(matcher, msg_name, Some(field.name())));
.any(|matcher| match_ident(matcher, fq_message_name, Some(field.name())));
if bytes {
BytesTy::Bytes
} else {
Expand Down
10 changes: 5 additions & 5 deletions prost-build/src/ident.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ pub fn to_upper_camel(s: &str) -> String {
}

/// Matches a 'matcher' against a fully qualified identifier.
pub fn match_ident(matcher: &str, msg: &str, field: Option<&str>) -> bool {
assert_eq!(b'.', msg.as_bytes()[0]);
pub fn match_ident(matcher: &str, fq_name: &str, field_name: Option<&str>) -> bool {
assert_eq!(b'.', fq_name.as_bytes()[0]);

if matcher.is_empty() {
return false;
Expand All @@ -52,9 +52,9 @@ pub fn match_ident(matcher: &str, msg: &str, field: Option<&str>) -> bool {

let match_paths = matcher.split('.').collect::<Vec<_>>();
let field_paths = {
let mut paths = msg.split('.').collect::<Vec<_>>();
if let Some(field) = field {
paths.push(field);
let mut paths = fq_name.split('.').collect::<Vec<_>>();
if let Some(field_name) = field_name {
paths.push(field_name);
}
paths
};
Expand Down
31 changes: 31 additions & 0 deletions prost-build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ pub struct Config {
out_dir: Option<PathBuf>,
extern_paths: Vec<(String, String)>,
protoc_args: Vec<OsString>,
disable_comments: Vec<String>,
}

impl Config {
Expand Down Expand Up @@ -410,6 +411,35 @@ impl Config {
self
}

/// Configures the code generator to omit documentation comments on generated Protobuf types.
///
/// # Example
///
/// Occasionally `.proto` files contain code blocks which are not valid Rust. To avoid doctest
/// failures, annotate the invalid code blocks with an [`ignore` or `no_run` attribute][1], or
/// disable doctests for the crate with a [Cargo.toml entry][2]. If neither of these options
/// are possible, then omit comments on generated code during doctest builds:
///
/// ```rust,ignore
/// let mut config = prost_build::Config::new();
/// config.disable_comments(".");
/// config.compile_protos(&["src/frontend.proto", "src/backend.proto"], &["src"])?;
/// ```
///
/// As with other options which take a set of paths, comments can be disabled on a per-package
/// or per-symbol basis.
///
/// [1]: https://doc.rust-lang.org/rustdoc/documentation-tests.html#attributes
/// [2]: https://doc.rust-lang.org/cargo/reference/cargo-targets.html#configuring-a-target
pub fn disable_comments<I, S>(&mut self, paths: I) -> &mut Self
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
self.disable_comments = paths.into_iter().map(|s| s.as_ref().to_string()).collect();
self
}

/// Declare an externally provided Protobuf package or type.
///
/// `extern_path` allows `prost` types in external crates to be referenced in generated code.
Expand Down Expand Up @@ -720,6 +750,7 @@ impl default::Default for Config {
out_dir: None,
extern_paths: Vec::new(),
protoc_args: Vec::new(),
disable_comments: Vec::new(),
}
}
}
Expand Down
3 changes: 0 additions & 3 deletions tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@ edition = "2018"

build = "src/build.rs"

[lib]
doctest = false

[features]
default = ["std"]
std = []
Expand Down
11 changes: 10 additions & 1 deletion tests/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,20 @@ fn main() {
.compile_protos(&[src.join("deprecated_field.proto")], includes)
.unwrap();

config
prost_build::Config::new()
.protoc_arg("--experimental_allow_proto3_optional")
.compile_protos(&[src.join("proto3_presence.proto")], includes)
.unwrap();

{
let mut config = prost_build::Config::new();
config.disable_comments(&["."]);

config
.compile_protos(&[src.join("invalid_doctest.proto")], includes)
.unwrap();
}

config
.compile_protos(&[src.join("well_known_types.proto")], includes)
.unwrap();
Expand Down
9 changes: 9 additions & 0 deletions tests/src/invalid_doctest.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
syntax = "proto3";

package invalid.doctest;

// ```
// invalid
// ```
message MessageWithInvalidDoctest {
}
6 changes: 6 additions & 0 deletions tests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@ pub mod proto3 {
}
}

pub mod invalid {
pub mod doctest {
include!(concat!(env!("OUT_DIR"), "/invalid.doctest.rs"));
}
}

use alloc::format;
use alloc::vec::Vec;

Expand Down

0 comments on commit a5bb7bf

Please sign in to comment.