Convert Request and ResponseWriter to interfaces #10
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
The primary purpose of this PR is to convert
Request
andResponseWriter
into interfaces that each contain a private method, preventing construction outside of this module.Our motivation for this started with wanting to not duplicate logic across
buf
andprotoplugin
. There's some work we have to do to support editions that effectively would be repeated in both modules, andprotoplugin
already largely copies the protoplugin package withinbuf
- it's how this module started in the first place. By centralizing the logic, we get one place to keep it up to date, and a concentrated set of (soon to be added) testing within this module to make sure it all works properly. We already had better validation withinprotoplugin
than we ever had inbuf
, and we want to take advantage of that while we work to support editions.buf
, however, does some special things that other plugin authors won't do. Specifically, it has the ability to take multipleCodeGeneratorRequests
, and output a singleCodeGeneratorResponse
after executing the equivalent of aHandler
in parallel. No one should do this outside ofbuf
- it's a super-speciality use-case, and creating parallelizableCodeGeneratorRequests
correctly is a challenge in itself - few need to proxy to other plugins, let alone in parallel. Part of whatbuf
does is call what amounts tonewRequest
for eachCodeGeneratorRequest
, and create a singleResponseWriter
that each parallel call calls to.Before this PR,
protoplugin
had theRequest
andResponseWriter
types as structs, without any useful ability to create these types outside of this package and use them. This was fine, as we purposefully wanted to limitHandler
invocation to theMain
andRun
functions. However, to take advantage ofprotoplugin
withinbuf
, and to allow the parallel execution use case, we had to do one of two things:protoplugin
itself. This seemed bad, and would dirty up the API for this single use case.Requests
andResponseWriters
in useful ways, and then support the use case ofHandlers
being invoked outside ofMain
orRun
.(2) seemed preferable, including outside of this specific use case - if we expose these
Request
andResponseWriter
types, and tell users to implement aHandler
type, it seems appropriate to let users invoke theseHandlers
(including in testing) on their own. However, a central tenant of this package is that users can rely onRequests
being validated, andResponseWriters
behaving appropriately. If we're telling users to construct their ownRequests
, andRequests
were structs, Golang idiomatic behavior would say thatrequest := &protoplugin.Request{}
should be a valid way to construct a request. We need to force users through a constructor, however, forRequest
in its current form (which we like) to make any sense - validation has to be performed, and we want users to rely on the fact thatRequests
are always valid.@mfridman and I discussed this approach offline and this seemed to make the most sense. @mfridman, this is the result of that - let me know what you think.
Of note, this also does the rename of
HandlerEnv
toPluginEnv
, and removes the usage of pointers forEnv
andPluginEnv
- the pointers don't really make sense in this case, and now that we're leaning into interfaces, there's less of an argument to use pointers to be consistent within this library.CompilerVersion
remains a pointer when returned fromRequest
, as presence is important.The API will likely evolve further from here - it's not great that there are two options
WithLenientValidation
andResponseWriterWithLenientValidation
. But this gets the basics in.