Patch protoc
plugin output with Go-specific features. The protoc-gen-go-patch
command wraps calls to Go code generators like protoc-gen-go
or protoc-gen-go-grpc
and patches the Go syntax before being written to disk.
go install github.com/alta/protopatch/cmd/protoc-gen-go-patch
After installing protoc-gen-go-patch
, use it by specifying it with a --go-patch_out=...
argument to protoc
:
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
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.
import "patch/go.proto";
message OldName {
option (go.message).name = 'NewName';
int id = 1 [(go.field).name = 'ID'];
}
enum Error {
option (go.enum).name = 'ProtocolError';
INVALID = 1 [(go.value).name = 'ErrInvalid'];
NOT_FOUND = 2 [(go.value).name = 'ErrNotFound'];
TOO_FUN = 3 [(go.value).name = 'ErrTooFun'];
}
Multiple options can be grouped together with a message bounded by {}
:
import "patch/go.proto";
message OldName {
option (go.message) = {name: 'NewName'};
int32 id = 1 [(go.field) = {name: 'ID'}];
}
enum Error {
option (go.enum) = {name: 'ProtocolError'};
INVALID = 1 [(go.value) = {name: 'ErrInvalid'}];
NOT_FOUND = 2 [(go.value) = {name: 'ErrNotFound'}];
TOO_FUN = 3 [(go.value) = {name: 'ErrTooFun'}];
}
message ToDo {
int32 id = 1 [(go.field).name: 'ID', (go.field).tags = 'xml:"id,attr"'];
string description = 2 [(go.field).tags: 'xml:"desc"'];
}
Multiple options can be grouped together with a message bounded by {}
:
message ToDo {
int32 id = 1 [(go.field) = {name: 'ID', tags: 'xml:"id,attr"'}];
string description = 2 [(go.field) = {tags: 'xml:"desc"'}];
}
A message field can be embedded in the generated Go struct with the (go.field).embed
option. This only works for message fields, and will not work for oneof fields or basic types.
import "patch/go.proto";
message A {
B b = 1 [(go.field).embed = true];
}
message B {
string value = 1;
}
The resulting Go struct will partially have the form:
type A struct {
*B
}
type B struct {
Value string
}
var a A
a.Value = "value" // This works because B is embedded in A
Multiple options can be grouped together with a message bounded by {}
:
import "patch/go.proto";
message A {
B b = 1 [(go.field) = {embed: true}];
}
message B {
string value = 1;
}
Protopatch can automatically “lint” generated names into something resembling idiomatic Go style. This feature should be considered unstable, and the names it generates are subject to change as this feature evolves.
- Initialisms: names with
ID
orURL
or other well-known initialisms will have their case preserved. For exampleId
would lint toID
, andApiBaseUrl
would lint toAPIBaseURL
. - Stuttering: it will attempt to remove repeated prefixed names from enum values. An enum value of type
Foo
namedFoo_FOO_BAR
would lint toFooBar
.
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.
option (go.lint).all = true;
option (go.lint).initialisms = 'RGB';
option (go.lint).initialisms = 'RGBA';
option (go.lint).initialisms = 'HSV';
enum Protocol {
// 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;
}
}