Skip to content

Commit

Permalink
Add support for (proto3) optional fields
Browse files Browse the repository at this point in the history
Fixes #2
  • Loading branch information
joeycumines committed Sep 25, 2022
1 parent c26800e commit 5c68093
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 10 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,9 @@ using this plugin.
| generated_filename_suffix | filename suffix for all generated files, defaults to `_copy.pb.go` |
| shallow_copy_method | method name generated for all message types unless set to an empty string, defaults to `Proto_ShallowCopy` |
| shallow_clone_method | method name generated for all message types unless set to an empty string, defaults to `Proto_ShallowClone` |

## Caveats

- As the (`protoc-gen-go`) generated getters don't support field presence (e.g. returning `bool` not `*bool`), generated
shallow copy methods cannot support, and therefore ignore, optional fields, for sources of a different type to the
destination
4 changes: 3 additions & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ go install

# generate the code, into the same directory as the proto file, see also the root readme, and google's reference docs
find examples -type f -name '*.proto' -exec \
protoc --proto_path=. --go_out=. --go_opt=paths=source_relative --go-copy_out=. --go-copy_opt=paths=source_relative \
protoc \
--experimental_allow_proto3_optional \
--proto_path=. --go_out=. --go_opt=paths=source_relative --go-copy_out=. --go-copy_opt=paths=source_relative \
{} +

# validate the generated code
Expand Down
2 changes: 1 addition & 1 deletion examples/addressbook/addressbook.proto
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
// https://github.com/protocolbuffers/protobuf/blob/master/examples/addressbook.proto

syntax = "proto3";
package tutorial;
package examples.addressbook;
option go_package = "github.com/joeycumines/protoc-gen-go-copy/examples/addressbook";

import "google/protobuf/timestamp.proto";
Expand Down
2 changes: 1 addition & 1 deletion examples/oneoftypes/oneoftypes.proto
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
syntax = "proto3";

package example;
package examples.oneoftypes;

option go_package = "github.com/joeycumines/protoc-gen-go-copy/examples/oneoftypes";

Expand Down
7 changes: 7 additions & 0 deletions examples/optionalfields/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# oneoftypes

Example of all basic types using the `optional` field rule.

This example was added as part of addressing [issue #2](https://github.com/joeycumines/protoc-gen-go-copy/issues/2).

See [../README.md](../README.md) for how to generate.
37 changes: 37 additions & 0 deletions examples/optionalfields/optionalfields.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
syntax = "proto3";

package examples.optionalfields;

option go_package = "github.com/joeycumines/protoc-gen-go-copy/examples/optionalfields";

message Example {
message A {
}
enum B {
B_UNSPECIFIED = 0;
}

// https://developers.google.com/protocol-buffers/docs/proto3#scalar

optional double double = 1;
optional float float = 2;
optional int32 int32 = 3;
optional int64 int64 = 4;
optional uint32 uint32 = 5;
optional uint64 uint64 = 6;
optional sint32 sint32 = 7;
optional sint64 sint64 = 8;
optional fixed32 fixed32 = 9;
optional fixed64 fixed64 = 10;
optional sfixed32 sfixed32 = 11;
optional sfixed64 sfixed64 = 12;
optional bool bool = 13;
optional string string = 14;
optional bytes bytes = 15;

optional A message = 21;
optional B enum = 22;
optional Example recursive = 23;

string non_optional = 31;
}
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ go 1.16

require (
github.com/jhump/gopoet v0.1.0
github.com/joeycumines/gopoet-protogen v0.1.0
google.golang.org/protobuf v1.27.1
github.com/joeycumines/gopoet-protogen v0.2.0
google.golang.org/protobuf v1.28.1
)
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/jhump/gopoet v0.1.0 h1:gYjOPnzHd2nzB37xYQZxj4EIQNpBrBskRqQQ3q4ZgSg=
github.com/jhump/gopoet v0.1.0/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+UPUI=
github.com/joeycumines/gopoet-protogen v0.1.0 h1:90o8BVwcIQJOvoGksGJpP/w2h5zoYuM0eQwEqLGOxVM=
github.com/joeycumines/gopoet-protogen v0.1.0/go.mod h1:xoERQVyp/Zw3/mRxPL+QKWpmFelsFCOncyGaG2JIhVk=
github.com/joeycumines/gopoet-protogen v0.2.0 h1:ht9tJtB0Gh86DDcDrH7x4NBaAavTMe9K/vygj7WSShc=
github.com/joeycumines/gopoet-protogen v0.2.0/go.mod h1:4WpQOy18ktTEG63rFFAmJP1e++0IuRnLrpTcDn7qmuc=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
31 changes: 30 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/joeycumines/gopoet-protogen"
"google.golang.org/protobuf/compiler/protogen"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/types/pluginpb"
"path"
)

Expand Down Expand Up @@ -70,6 +71,8 @@ func (x *Generator) NewFlagSet() *flag.FlagSet {
}

func (x Generator) Generate() error {
x.Plugin.SupportedFeatures = uint64(pluginpb.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL)

for _, file := range x.Plugin.Files {
x.Cache.AddFile(file)

Expand Down Expand Up @@ -98,16 +101,41 @@ func (x Generator) Generate() error {
elem.SetComment(fmt.Sprintf(`%s copies fields, from v to the receiver, using field getters.
Note that v is of an arbitrary type, which may implement any number of the
field getters, which are defined as any methods of the same signature as those
generated for the receiver type, with a name starting with Get.`, x.ShallowCopyMethod))
generated for the receiver type, with a name starting with Get.
WARNING: Optional fields may be ignored, if v is not the receiver type.`, x.ShallowCopyMethod))
elem.AddArg(`v`, gopoet.InterfaceType(nil))
if len(fields) != 0 {
elem.Printlnf(`switch v := v.(type) {`)
elem.Printlnf(`case %s:`, gopoet.PointerType(t))
var hasOptional bool
for _, field := range fields {
if gopoet_protogen.FieldIsOptional(field) {
hasOptional = true
continue
}
elem.Printlnf(`x.%s = v.%s()`, field.Name(), field.Getter().Name)
}
if hasOptional {
elem.Println(`if v != nil {`)
for _, field := range fields {
if gopoet_protogen.FieldIsOptional(field) {
elem.Printlnf(`x.%s = v.%s`, field.Name(), field.Name())
}
}
elem.Println(`} else {`)
for _, field := range fields {
if gopoet_protogen.FieldIsOptional(field) {
elem.Printlnf(`x.%s = nil`, field.Name())
}
}
elem.Println(`}`)
}
elem.Println(`default:`)
for _, field := range fields {
if gopoet_protogen.FieldIsOptional(field) {
// special case: the generated getters don't support presence, so we can't use them
continue
}
elem.Printlnf(`if v, ok := v.(%s); ok {`, gopoet.InterfaceType(nil, field.Getter()))
elem.Printlnf(`x.%s = v.%s()`, field.Name(), field.Getter().Name)
if field.OneOf() != nil {
Expand Down Expand Up @@ -154,6 +182,7 @@ generated for the receiver type, with a name starting with Get.`, x.ShallowCopyM
genMsg(msg)
}
}

for _, msg := range file.Messages {
genMsg(msg)
}
Expand Down

0 comments on commit 5c68093

Please sign in to comment.