# protopatch [![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white)](https://pkg.go.dev/github.com/alta/protopatch) [![build status](https://img.shields.io/github/workflow/status/alta/protopatch/Go.svg)](https://github.com/alta/protopatch/actions) Patch `protoc` plugin output with Go-specific features. The `protoc-gen-go-patch` command wraps calls to Go code generators like [`protoc-gen-go`](https://pkg.go.dev/google.golang.org/protobuf/cmd/protoc-gen-go) or [`protoc-gen-go-grpc`](https://pkg.go.dev/google.golang.org/grpc/cmd/protoc-gen-go-grpc) and patches the Go syntax before being written to disk. ## Install `go install github.com/alta/protopatch/cmd/protoc-gen-go-patch` ## Usage After installing `protoc-gen-go-patch`, use it by specifying it with a `--go-patch_out=...` argument to `protoc`: ```shell protoc \ -I . \ -I `go list -m -f {{.Dir}} github.com/alta/protopatch` \ -I `go list -m -f {{.Dir}} google.golang.org/protobuf` \ --go-patch_out=plugin=go,paths=source_relative:. \ --go-patch_out=plugin=go-grpc,paths=source_relative:. \ *.proto ``` ## Features Patches are defined via an `Options` extension on messages, fields, `oneof` fields, enums, and enum values. - `go.message` — message options, which modify the generated Go struct for a message. - `go.field` — message field options, which modify Go struct fields and getter methods. - `go.oneof` — oneof field options, which modify struct fields, interface types, and wrapper types. - `go.enum` — enum options, which modify Go enum types and values. - `go.value` — enum value options, which modify Go const values. ### Custom Names ```proto import "patch/go.proto"; message OldName { option (go.message).name = 'NewName'; int id = 1 [(go.field).name = 'ID']; } enum Errors { option (go.enum).name = 'ProtocolErrors'; INVALID = 1 [(go.value).name = 'ErrInvalid']; NOT_FOUND = 2 [(go.value).name = 'ErrNotFound']; TOO_FUN = 3 [(go.value).name = 'ErrTooFun']; } ``` #### Alternate Syntax Multiple options can be grouped together with a message bounded by `{}`: ```proto import "patch/go.proto"; message OldName { option (go.message) = {name: 'NewName'}; int id = 1 [(go.field) = {name: 'ID'}]; } enum Errors { option (go.enum) = {name: 'ProtocolErrors'}; INVALID = 1 [(go.value) = {name: 'ErrInvalid'}]; NOT_FOUND = 2 [(go.value) = {name: 'ErrNotFound'}]; TOO_FUN = 3 [(go.value) = {name: 'ErrTooFun'}]; } ``` ### Struct Tags ```proto message ToDo { int32 id = 1 [(go.field).name: 'ID', (go.field).tags = 'xml:"id,attr"']; string description = 2 [(go.field).tags: 'xml:"desc"']; } ``` #### Alternate Syntax Multiple options can be grouped together with a message bounded by `{}`: ```proto message ToDo { int32 id = 1 [(go.field) = {name: 'ID', tags: 'xml:"id,attr"'}]; string description = 2 [(go.field) = {tags: 'xml:"desc"'}]; } ``` ### Linting Protopatch can automatically “lint” generated names into something resembling [idiomatic Go style](https://golang.org/doc/effective_go.html#names). This feature should be considered *unstable*, and the names it generates are subject to change as this feature evolves. - **Initialisms:** names with `ID` or `URL` or other well-known initialisms will have their case preserved. For example `Id` would lint to `ID`, and `ApiBaseUrl` would lint to `APIBaseURL`. - **Stuttering:** it will attempt to remove repeated prefixed names from enum values. An enum value of type `Foo` named `Foo_FOO_BAR` would lint to `FooBar`. To lint all generated Go names, add `option (go.lint).all = true` to your `proto` file. To lint only enum values, add `option (go.lint).values = true`. To specify one or more custom initialisms, specify an initialism with `option (go.lint).initialisms = 'HSV'` for the `HSV` initialism. All names with `HSV` will preserve its case. ```proto option (go.lint).all = true; option (go.lint).initialisms = 'RGB'; option (go.lint).initialisms = 'RGBA'; option (go.lint).initialisms = 'HSV'; enum Color { // PROTOCOL_INVALID value should lint to ProtocolInvalid. PROTOCOL_INVALID = 0; // PROTOCOL_IP value should lint to ProtocolIP. PROTOCOL_IP = 1; // PROTOCOL_UDP value should lint to ProtocolUDP. PROTOCOL_UDP = 2; // PROTOCOL_TCP value should lint to ProtocolTCP. PROTOCOL_TCP = 3; } message Color { oneof value { // rgb should lint to RGB. string rgb = 1; // rgba should lint to RGBA. string rgba = 2; // hsv should lint to HSV. string hsv = 3; } } ```