-
Notifications
You must be signed in to change notification settings - Fork 525
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Clean up CodeGenerator
by moving compilation-global data and logic to Context
#1190
base: master
Are you sure you want to change the base?
Conversation
205a126
to
7a01e89
Compare
I would like this to be merged before I add an enum type registry, which is necessary to properly implement imports (and later with editions, the |
7a01e89
to
75d81e1
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see plenty of internal changes that I like: better naming, cleanup of boxed, more helper functions.
- Is it possible to separate those from the API changes? Then we can that merge that before doing a version number bump
- Is it possible to separate each improvement into a separate commit? That will make reviewing easier.
prost-build/src/code_generator.rs
Outdated
buf: &mut String, | ||
) { | ||
impl<'a, 'b> CodeGenerator<'a, 'b> { | ||
pub fn generate(context: &mut Context<'b>, file: FileDescriptorProto, buf: &mut String) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an API breaking change, right? I don't understand how the Context
instance should be created by prost
users as there is no documentation changes included.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, CodeGenerator
is private to the crate, as is the newly added Context
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see: Config::generate
is public, but CodeGenerator
is private. Can you confirm that no breaking change happened using cargo semver-checks
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do. I also think it would help to make this method pub(crate)
to alleviate any confusion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes pub(crate)
is a great idea
pub struct CodeGenerator<'a> { | ||
config: &'a mut Config, | ||
pub struct CodeGenerator<'a, 'b> { | ||
context: &'a mut Context<'b>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand why some fields are in CodeGenerator
, Context
or Config
. For example, depth
also feels like it could be part of Context
.
Can you explain when a field should be in each of those structs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Config
is the public settings configured by the API user.
Context
is a private helper that captures data global to the compilation requests: the message graph, the map of extern paths, and the reference to the Config
. I also plan to add a map of enum openness flags for a proper implementation of open enums in #1079.
It is populated before code generation and the instance is shared between successive calls to CodeGenerator::generate
for the requested members. This replaces several arguments that did the same thing with the disparate data structures.
The owned members of CodeGenerator
manage state specific to code generation for a proto file. So depth
belongs there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So Context
fields are used for multiple files and CodeGenerator
fields are discarded after a single file. That makes sense.
Can you add this explanation as a doc comment to the types?
pub struct CodeGenerator<'a> { | ||
config: &'a mut Config, | ||
pub struct CodeGenerator<'a, 'b> { | ||
context: &'a mut Context<'b>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Config
is the public settings configured by the API user.
Context
is a private helper that captures data global to the compilation requests: the message graph, the map of extern paths, and the reference to the Config
. I also plan to add a map of enum openness flags for a proper implementation of open enums in #1079.
It is populated before code generation and the instance is shared between successive calls to CodeGenerator::generate
for the requested members. This replaces several arguments that did the same thing with the disparate data structures.
The owned members of CodeGenerator
manage state specific to code generation for a proto file. So depth
belongs there.
CodeGenerator
by moving compilation-global data and logic to Context
Move global context data and logic around it from CodeGenerator to a new Context struct. Also move the logic of can_message_derive_copy and can_field_derive_copy from MessageGraph where it caused some data duplication.
75d81e1
to
1218d3a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not every line is improved, but I think this is an improvement overall. I have a few questions that I like to be answered before merging.
pub struct CodeGenerator<'a> { | ||
config: &'a mut Config, | ||
pub struct CodeGenerator<'a, 'b> { | ||
context: &'a mut Context<'b>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So Context
fields are used for multiple files and CodeGenerator
fields are discarded after a single file. That makes sense.
Can you add this explanation as a doc comment to the types?
prost-build/src/code_generator.rs
Outdated
buf: &mut String, | ||
) { | ||
impl<'a, 'b> CodeGenerator<'a, 'b> { | ||
pub fn generate(context: &mut Context<'b>, file: FileDescriptorProto, buf: &mut String) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see: Config::generate
is public, but CodeGenerator
is private. Can you confirm that no breaking change happened using cargo semver-checks
?
self.buf.push_str(&format!( | ||
"impl {}::Name for {} {{\n", | ||
self.config.prost_path.as_deref().unwrap_or("::prost"), | ||
"impl {prost_path}::Name for {} {{\n", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this. It improves readability.
prost-build/src/code_generator.rs
Outdated
@@ -297,15 +285,16 @@ impl CodeGenerator<'_> { | |||
self.pop_mod(); | |||
} | |||
|
|||
if self.config.enable_type_names { | |||
if self.context.config().enable_type_names { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels like a decrease in readability. It adds an extra layer .context
and an extra indirection config()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can add a .config()
helper method to CodeGenerator
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's leave it like this for now. I wanted to point to a concrete example where I think readability suffered. But I think this PR is a net positive.
It was not obvious immediately from the declaration that CodeGenerator is private to the crate.
Add doc comments clarifying the lifetime of `Context` and `CodeGenerator` in the code generation process.
Rename to service_generator_mut so it's evident that we are obtaining a mutable reference even though the Context reference is shared.
Avoid behavioral changes in the Context refactoring.
Avoid behavioral changes in the Context refactoring.
To please clippy.
34cf0af
to
f798503
Compare
Refactoring improvements to prost-build:
CodeGenerator
to a newContext
struct.Context
fromMessageGraph
, where it caused some data duplication.Config
reference needing to be mutable for its user-provided service generator, by exposing both fromContext
in a more disciplined way.This also fixes a bug in the logic that determines whether
Copy
can be derived on a message, where aboxed
config rule matching a field that is part of a oneof would be disregarded.