Skip to content

Commit

Permalink
prost-build: CodeGenerator::boxed method (#1019)
Browse files Browse the repository at this point in the history
* prost-build: CodeGenerator::boxed method

A helper method to capture the logic of deciding whether a field
needs to be boxed. This follows the pattern with other methods like
`optional`, and will allow reusing the logic in the upcoming builder
codegen.

* prost-build: more reuse with CodeGenerator::boxed

The bit in CodeGenerator::append_oneof was pretty much the same,
except the configuration is looked up for the oneof name.

Rearrange the logic so that intermediate values are bound when needed.
If a repeated fields is configured to be boxed, a deprecation warning
will be emitted by the build script.

* prost-build: document CodeGenerator::boxed

* prost-build: only emit warning on repeated fields

Co-authored-by: Casper Meijn <casper@meijn.net>

---------

Co-authored-by: Casper Meijn <casper@meijn.net>
  • Loading branch information
mzabaluev and caspermeijn committed May 5, 2024
1 parent 2308ba4 commit 1f38ea6
Showing 1 changed file with 49 additions and 22 deletions.
71 changes: 49 additions & 22 deletions prost-build/src/code_generator.rs
Expand Up @@ -402,19 +402,9 @@ impl<'a> CodeGenerator<'a> {
let repeated = field.descriptor.label == Some(Label::Repeated as i32);
let deprecated = self.deprecated(&field.descriptor);
let optional = self.optional(&field.descriptor);
let boxed = self.boxed(&field.descriptor, fq_message_name, None);
let ty = self.resolve_type(&field.descriptor, fq_message_name);

let boxed = !repeated
&& ((type_ == Type::Message || type_ == Type::Group)
&& self
.message_graph
.is_nested(field.descriptor.type_name(), fq_message_name))
|| (self
.config
.boxed
.get_first_field(fq_message_name, field.descriptor.name())
.is_some());

debug!(
" field: {:?}, type: {:?}, boxed: {}",
field.descriptor.name(),
Expand Down Expand Up @@ -636,8 +626,6 @@ impl<'a> CodeGenerator<'a> {
self.path.push(2);
self.depth += 1;
for field in &oneof.fields {
let type_ = field.descriptor.r#type();

self.path.push(field.path_index);
self.append_doc(fq_message_name, Some(field.descriptor.name()));
self.path.pop();
Expand All @@ -654,15 +642,11 @@ impl<'a> CodeGenerator<'a> {
self.push_indent();
let ty = self.resolve_type(&field.descriptor, fq_message_name);

let boxed = ((type_ == Type::Message || type_ == Type::Group)
&& self
.message_graph
.is_nested(field.descriptor.type_name(), fq_message_name))
|| (self
.config
.boxed
.get_first_field(&oneof_name, field.descriptor.name())
.is_some());
let boxed = self.boxed(
&field.descriptor,
fq_message_name,
Some(oneof.descriptor.name()),
);

debug!(
" oneof: {:?}, type: {:?}, boxed: {}",
Expand Down Expand Up @@ -1068,6 +1052,49 @@ impl<'a> CodeGenerator<'a> {
}
}

/// Returns whether the Rust type for this field needs to be `Box<_>`.
///
/// This can be explicitly configured with `Config::boxed`, or necessary
/// to prevent an infinitely sized type definition in case when the type of
/// a non-repeated message field transitively contains the message itself.
fn boxed(
&self,
field: &FieldDescriptorProto,
fq_message_name: &str,
oneof: Option<&str>,
) -> bool {
let repeated = field.label == Some(Label::Repeated as i32);
let fd_type = field.r#type();
if !repeated
&& (fd_type == Type::Message || fd_type == Type::Group)
&& self
.message_graph
.is_nested(field.type_name(), fq_message_name)
{
return true;
}
let config_path = match oneof {
None => Cow::Borrowed(fq_message_name),
Some(ooname) => Cow::Owned(format!("{fq_message_name}.{ooname}")),
};
if self
.config
.boxed
.get_first_field(&config_path, field.name())
.is_some()
{
if repeated {
println!(
"cargo:warning=\
Field X is repeated and manually marked as boxed. \
This is deprecated and support will be removed in a later release"
);
}
return true;
}
false
}

/// Returns `true` if the field options includes the `deprecated` option.
fn deprecated(&self, field: &FieldDescriptorProto) -> bool {
field
Expand Down

0 comments on commit 1f38ea6

Please sign in to comment.