Skip to content

Commit

Permalink
Convert Request and ResponseWriter to interfaces (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
bufdev authored Mar 18, 2024
1 parent 1de7ea8 commit 6e44eff
Show file tree
Hide file tree
Showing 10 changed files with 302 additions and 234 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,9 @@ func main() {

func handle(
_ context.Context,
_ *protoplugin.HandlerEnv,
responseWriter *protoplugin.ResponseWriter,
request *protoplugin.Request,
_ protoplugin.PluginEnv,
responseWriter protoplugin.ResponseWriter,
request protoplugin.Request,
) error {
// Set the flag indicating that we support proto3 optionals. We don't even use them in this
// plugin, but protoc will error if it encounters a proto3 file with an optional but the
Expand Down Expand Up @@ -193,7 +193,7 @@ Errors or warnings will also be produced if:
a duplicate name is plugin authoring issue, and here at Buf, we've seen a lot of plugins have this issue!
- Any file path is not cleaned.

By default, these are errors, however if `WithLenientResponseValidation` is set, these will be warnings.
By default, these are errors, however if `WithLenientValidation` is set, these will be warnings.

## What this library is not

Expand Down
8 changes: 4 additions & 4 deletions env.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ package protoplugin

import "io"

// Env represents an environment for a plugin to run within.
// Env represents an environment.
//
// This wraps items like args, environment variables, and stdio.
//
Expand All @@ -37,13 +37,13 @@ type Env struct {
Stderr io.Writer
}

// HandlerEnv represents an environment that a Handler is run within.
// PluginEnv represents an environment that a plugin is run within.
//
// This provides the environment variables and stderr to a Handler. A Handler should not have
// This provides the environment variables and stderr. A plugin implementation should not have
// access to stdin, stdout, or the args, as these are controlled by the plugin framework.
//
// When calling Main, this uses the values os.Environ and os.Stderr.
type HandlerEnv struct {
type PluginEnv struct {
// Environment are the environment variables.
Environ []string
// Stderr is the stderr for the plugin.
Expand Down
16 changes: 8 additions & 8 deletions handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,21 +44,21 @@ type Handler interface {
// (for example, a missing option), this error should be added to the response via SetError.
Handle(
ctx context.Context,
handlerEnv *HandlerEnv,
responseWriter *ResponseWriter,
request *Request,
pluginEnv PluginEnv,
responseWriter ResponseWriter,
request Request,
) error
}

// HandlerFunc is a function that implements Handler.
type HandlerFunc func(context.Context, *HandlerEnv, *ResponseWriter, *Request) error
type HandlerFunc func(context.Context, PluginEnv, ResponseWriter, Request) error

// Handle implements Handler.
func (h HandlerFunc) Handle(
ctx context.Context,
handlerEnv *HandlerEnv,
responseWriter *ResponseWriter,
request *Request,
pluginEnv PluginEnv,
responseWriter ResponseWriter,
request Request,
) error {
return h(ctx, handlerEnv, responseWriter, request)
return h(ctx, pluginEnv, responseWriter, request)
}
6 changes: 3 additions & 3 deletions internal/examples/protoc-gen-protogen-simple/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ func main() {

func handle(
_ context.Context,
_ *protoplugin.HandlerEnv,
responseWriter *protoplugin.ResponseWriter,
request *protoplugin.Request,
_ protoplugin.PluginEnv,
responseWriter protoplugin.ResponseWriter,
request protoplugin.Request,
) error {
plugin, err := protogen.Options{}.New(request.CodeGeneratorRequest())
if err != nil {
Expand Down
6 changes: 3 additions & 3 deletions internal/examples/protoc-gen-protoreflect-simple/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ func main() {

func handle(
_ context.Context,
_ *protoplugin.HandlerEnv,
responseWriter *protoplugin.ResponseWriter,
request *protoplugin.Request,
_ protoplugin.PluginEnv,
responseWriter protoplugin.ResponseWriter,
request protoplugin.Request,
) error {
// Set the flag indicating that we support proto3 optionals. We don't even use them in this
// plugin, but protoc will error if it encounters a proto3 file with an optional but the
Expand Down
6 changes: 3 additions & 3 deletions internal/examples/protoc-gen-simple/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ func main() {

func handle(
_ context.Context,
_ *protoplugin.HandlerEnv,
responseWriter *protoplugin.ResponseWriter,
request *protoplugin.Request,
_ protoplugin.PluginEnv,
responseWriter protoplugin.ResponseWriter,
request protoplugin.Request,
) error {
// Set the flag indicating that we support proto3 optionals. We don't even use them in this
// plugin, but protoc will error if it encounters a proto3 file with an optional but the
Expand Down
57 changes: 23 additions & 34 deletions protoplugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import (

var (
// osEnv is the os-based Env used in Main.
osEnv = &Env{
osEnv = Env{
Args: os.Args[1:],
Environ: os.Environ(),
Stdin: os.Stdin,
Expand Down Expand Up @@ -77,7 +77,7 @@ func Main(handler Handler, options ...MainOption) {
// when writing plugin tests, or if you want to use your own custom logic for main functions.
func Run(
ctx context.Context,
env *Env,
env Env,
handler Handler,
options ...RunOption,
) error {
Expand Down Expand Up @@ -114,8 +114,8 @@ func WithVersion(version string) RunOption {
})
}

// WithLenientResponseValidation returns a new GenerateOption that says handle non-critical issues with response
// construction as warnings that will be handled by the given warning handler.
// WithLenientValidation returns a new RunOption that says handle non-critical issues
// as warnings that will be handled by the given warning handler.
//
// This allows the following issues to result in warnings instead of errors:
//
Expand All @@ -137,19 +137,19 @@ func WithVersion(version string) RunOption {
//
// The default is to error on these issues.
//
// Implementers of lenientResponseValidationErrorFunc can assume that errors passed will be non-nil and have non-empty
// Implementers of lenientValidationErrorFunc can assume that errors passed will be non-nil and have non-empty
// values for err.Error().
func WithLenientResponseValidation(lenientResponseValidateErrorFunc func(error)) RunOption {
func WithLenientValidation(lenientValidateErrorFunc func(error)) RunOption {
return optsFunc(func(opts *opts) {
opts.lenientResponseValidateErrorFunc = lenientResponseValidateErrorFunc
opts.lenientValidateErrorFunc = lenientValidateErrorFunc
})
}

/// *** PRIVATE ***

func run(
ctx context.Context,
env *Env,
env Env,
handler Handler,
opts *opts,
) error {
Expand All @@ -173,16 +173,23 @@ func run(
if err := proto.Unmarshal(input, codeGeneratorRequest); err != nil {
return err
}
codeGeneratorResponse, err := generate(
request, err := NewRequest(codeGeneratorRequest)
if err != nil {
return err
}
responseWriter := NewResponseWriter(ResponseWriterWithLenientValidation(opts.lenientValidateErrorFunc))
if err := handler.Handle(
ctx,
&HandlerEnv{
PluginEnv{
Environ: env.Environ,
Stderr: env.Stderr,
},
codeGeneratorRequest,
handler,
opts,
)
responseWriter,
request,
); err != nil {
return err
}
codeGeneratorResponse, err := responseWriter.ToCodeGeneratorResponse()
if err != nil {
return err
}
Expand All @@ -194,24 +201,6 @@ func run(
return err
}

func generate(
ctx context.Context,
handlerEnv *HandlerEnv,
codeGeneratorRequest *pluginpb.CodeGeneratorRequest,
handler Handler,
opts *opts,
) (*pluginpb.CodeGeneratorResponse, error) {
request, err := newRequest(codeGeneratorRequest)
if err != nil {
return nil, err
}
responseWriter := newResponseWriter(opts.lenientResponseValidateErrorFunc)
if err := handler.Handle(ctx, handlerEnv, responseWriter, request); err != nil {
return nil, err
}
return responseWriter.toCodeGeneratorResponse()
}

// withCancelInterruptSignal returns a context that is cancelled if interrupt signals are sent.
func withCancelInterruptSignal(ctx context.Context) (context.Context, context.CancelFunc) {
interruptSignalC, closer := newInterruptSignalChannel()
Expand All @@ -237,8 +226,8 @@ func newInterruptSignalChannel() (<-chan os.Signal, func()) {
}

type opts struct {
version string
lenientResponseValidateErrorFunc func(error)
version string
lenientValidateErrorFunc func(error)
}

func newOpts() *opts {
Expand Down
12 changes: 6 additions & 6 deletions protoplugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ func TestBasic(t *testing.T) {
HandlerFunc(
func(
ctx context.Context,
handlerEnv *HandlerEnv,
responseWriter *ResponseWriter,
request *Request,
pluginEnv PluginEnv,
responseWriter ResponseWriter,
request Request,
) error {
for _, fileDescriptorProto := range request.FileDescriptorProtosToGenerate() {
topLevelMessageNames := make([]string, len(fileDescriptorProto.GetMessageType()))
Expand Down Expand Up @@ -77,14 +77,14 @@ func TestWithVersionOption(t *testing.T) {
stdout := bytes.NewBuffer(nil)
err := Run(
context.Background(),
&Env{
Env{
Args: args,
Environ: nil,
Stdin: iotest.ErrReader(io.EOF),
Stdout: stdout,
Stderr: io.Discard,
},
HandlerFunc(func(ctx context.Context, _ *HandlerEnv, _ *ResponseWriter, _ *Request) error { return nil }),
HandlerFunc(func(ctx context.Context, _ PluginEnv, _ ResponseWriter, _ Request) error { return nil }),
runOptions...,
)
return stdout.String(), err
Expand Down Expand Up @@ -129,7 +129,7 @@ func testBasic(

err = Run(
ctx,
&Env{
Env{
Args: nil,
Environ: nil,
Stdin: stdin,
Expand Down
Loading

0 comments on commit 6e44eff

Please sign in to comment.