From d7fa79eab0f0de57c8e3812f3ff5fd86cb74117a Mon Sep 17 00:00:00 2001 From: Oliver Sun Date: Wed, 1 Nov 2023 12:25:13 -0400 Subject: [PATCH] Update oneof required semantics to be exactly one field passes field required --- .../validate/conformance/cases/oneofs.proto | 2 + .../protovalidate/buf/validate/validate.proto | 8 +- .../validate/conformance/cases/oneofs.pb.go | 104 +++++++++++------- .../internal/gen/buf/validate/validate.pb.go | 8 +- .../internal/cases/cases_oneof.go | 15 ++- 5 files changed, 88 insertions(+), 49 deletions(-) diff --git a/proto/protovalidate-testing/buf/validate/conformance/cases/oneofs.proto b/proto/protovalidate-testing/buf/validate/conformance/cases/oneofs.proto index b9a87438..8eab8a36 100644 --- a/proto/protovalidate-testing/buf/validate/conformance/cases/oneofs.proto +++ b/proto/protovalidate-testing/buf/validate/conformance/cases/oneofs.proto @@ -17,6 +17,7 @@ syntax = "proto3"; package buf.validate.conformance.cases; import "buf/validate/validate.proto"; +import "google/protobuf/duration.proto"; message TestOneofMsg { bool val = 1 [(buf.validate.field).bool.const = true]; @@ -45,6 +46,7 @@ message OneofRequired { int32 y = 2; int32 name_with_underscores = 3; int32 under_and_1_number = 4; + google.protobuf.Duration duration = 5; } } diff --git a/proto/protovalidate/buf/validate/validate.proto b/proto/protovalidate/buf/validate/validate.proto index 9b4c7b4f..57806832 100644 --- a/proto/protovalidate/buf/validate/validate.proto +++ b/proto/protovalidate/buf/validate/validate.proto @@ -92,12 +92,12 @@ message MessageConstraints { // The `OneofConstraints` message type enables you to manage constraints for // oneof fields in your protobuf messages. Use the `required` constraint to ensure -// that exactly one of the fields within a oneof is set; validation will fail -// if none of the fields in the oneof are set: +// that exactly one of the fields within a oneof is not empty; validation will fail +// if all of the fields in the oneof are empty: message OneofConstraints { // `required` is an optional boolean attribute that ensures that - // exactly one of the field options in a oneof is set; validation fails if - // no fields in the oneof are set. + // exactly one of the field options in a oneof is not empty; validation fails if + // all fields in the oneof are empty. // // ```proto // message MyMessage { diff --git a/tools/internal/gen/buf/validate/conformance/cases/oneofs.pb.go b/tools/internal/gen/buf/validate/conformance/cases/oneofs.pb.go index c69208cc..68254116 100644 --- a/tools/internal/gen/buf/validate/conformance/cases/oneofs.pb.go +++ b/tools/internal/gen/buf/validate/conformance/cases/oneofs.pb.go @@ -24,6 +24,7 @@ import ( _ "github.com/bufbuild/protovalidate/tools/internal/gen/buf/validate" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + durationpb "google.golang.org/protobuf/types/known/durationpb" reflect "reflect" sync "sync" ) @@ -269,6 +270,7 @@ type OneofRequired struct { // *OneofRequired_Y // *OneofRequired_NameWithUnderscores // *OneofRequired_UnderAnd_1Number + // *OneofRequired_Duration O isOneofRequired_O `protobuf_oneof:"o"` } @@ -339,6 +341,13 @@ func (x *OneofRequired) GetUnderAnd_1Number() int32 { return 0 } +func (x *OneofRequired) GetDuration() *durationpb.Duration { + if x, ok := x.GetO().(*OneofRequired_Duration); ok { + return x.Duration + } + return nil +} + type isOneofRequired_O interface { isOneofRequired_O() } @@ -359,6 +368,10 @@ type OneofRequired_UnderAnd_1Number struct { UnderAnd_1Number int32 `protobuf:"varint,4,opt,name=under_and_1_number,json=underAnd1Number,proto3,oneof"` } +type OneofRequired_Duration struct { + Duration *durationpb.Duration `protobuf:"bytes,5,opt,name=duration,proto3,oneof"` +} + func (*OneofRequired_X) isOneofRequired_O() {} func (*OneofRequired_Y) isOneofRequired_O() {} @@ -367,6 +380,8 @@ func (*OneofRequired_NameWithUnderscores) isOneofRequired_O() {} func (*OneofRequired_UnderAnd_1Number) isOneofRequired_O() {} +func (*OneofRequired_Duration) isOneofRequired_O() {} + type OneofIgnoreEmpty struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -471,7 +486,9 @@ var file_buf_validate_conformance_cases_oneofs_proto_rawDesc = []byte{ 0x75, 0x66, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x73, 0x65, 0x73, 0x1a, 0x1b, 0x62, 0x75, 0x66, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x29, 0x0a, 0x0c, 0x54, 0x65, + 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x29, 0x0a, 0x0c, 0x54, 0x65, 0x73, 0x74, 0x4f, 0x6e, 0x65, 0x6f, 0x66, 0x4d, 0x73, 0x67, 0x12, 0x19, 0x0a, 0x03, 0x76, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x42, 0x07, 0xba, 0x48, 0x04, 0x6a, 0x02, 0x08, 0x01, 0x52, 0x03, 0x76, 0x61, 0x6c, 0x22, 0x30, 0x0a, 0x09, 0x4f, 0x6e, 0x65, 0x6f, 0x66, 0x4e, 0x6f, @@ -485,7 +502,7 @@ var file_buf_validate_conformance_cases_oneofs_proto_rawDesc = []byte{ 0x32, 0x2c, 0x2e, 0x62, 0x75, 0x66, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x73, 0x65, 0x73, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x4f, 0x6e, 0x65, 0x6f, 0x66, 0x4d, 0x73, 0x67, 0x48, 0x00, - 0x52, 0x01, 0x7a, 0x42, 0x03, 0x0a, 0x01, 0x6f, 0x22, 0xa0, 0x01, 0x0a, 0x0d, 0x4f, 0x6e, 0x65, + 0x52, 0x01, 0x7a, 0x42, 0x03, 0x0a, 0x01, 0x6f, 0x22, 0xd9, 0x01, 0x0a, 0x0d, 0x4f, 0x6e, 0x65, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x0e, 0x0a, 0x01, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x01, 0x78, 0x12, 0x0e, 0x0a, 0x01, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x48, 0x00, 0x52, 0x01, 0x79, 0x12, 0x34, 0x0a, 0x15, 0x6e, 0x61, @@ -494,34 +511,38 @@ var file_buf_validate_conformance_cases_oneofs_proto_rawDesc = []byte{ 0x65, 0x57, 0x69, 0x74, 0x68, 0x55, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x31, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x48, 0x00, 0x52, 0x0f, - 0x75, 0x6e, 0x64, 0x65, 0x72, 0x41, 0x6e, 0x64, 0x31, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x42, - 0x0a, 0x0a, 0x01, 0x6f, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x01, 0x22, 0x73, 0x0a, 0x10, 0x4f, - 0x6e, 0x65, 0x6f, 0x66, 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, - 0x1c, 0x0a, 0x01, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0c, 0xba, 0x48, 0x09, 0xd0, - 0x01, 0x01, 0x72, 0x04, 0x10, 0x03, 0x18, 0x05, 0x48, 0x00, 0x52, 0x01, 0x78, 0x12, 0x1c, 0x0a, - 0x01, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x0c, 0xba, 0x48, 0x09, 0xd0, 0x01, 0x01, - 0x7a, 0x04, 0x10, 0x03, 0x18, 0x05, 0x48, 0x00, 0x52, 0x01, 0x79, 0x12, 0x1e, 0x0a, 0x01, 0x7a, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x42, 0x0e, 0xba, 0x48, 0x0b, 0xd0, 0x01, 0x01, 0x1a, 0x06, - 0x18, 0x80, 0x01, 0x28, 0x80, 0x02, 0x48, 0x00, 0x52, 0x01, 0x7a, 0x42, 0x03, 0x0a, 0x01, 0x6f, - 0x42, 0xa2, 0x02, 0x0a, 0x22, 0x63, 0x6f, 0x6d, 0x2e, 0x62, 0x75, 0x66, 0x2e, 0x76, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, - 0x65, 0x2e, 0x63, 0x61, 0x73, 0x65, 0x73, 0x42, 0x0b, 0x4f, 0x6e, 0x65, 0x6f, 0x66, 0x73, 0x50, - 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x53, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x62, 0x75, 0x66, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x2f, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x62, 0x75, 0x66, - 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x6f, 0x72, - 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x2f, 0x63, 0x61, 0x73, 0x65, 0x73, 0xa2, 0x02, 0x04, 0x42, 0x56, - 0x43, 0x43, 0xaa, 0x02, 0x1e, 0x42, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x65, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x43, 0x61, - 0x73, 0x65, 0x73, 0xca, 0x02, 0x1e, 0x42, 0x75, 0x66, 0x5c, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x65, 0x5c, 0x43, 0x6f, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x5c, 0x43, - 0x61, 0x73, 0x65, 0x73, 0xe2, 0x02, 0x2a, 0x42, 0x75, 0x66, 0x5c, 0x56, 0x61, 0x6c, 0x69, 0x64, - 0x61, 0x74, 0x65, 0x5c, 0x43, 0x6f, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x5c, - 0x43, 0x61, 0x73, 0x65, 0x73, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0xea, 0x02, 0x21, 0x42, 0x75, 0x66, 0x3a, 0x3a, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x65, 0x3a, 0x3a, 0x43, 0x6f, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x3a, 0x3a, - 0x43, 0x61, 0x73, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x75, 0x6e, 0x64, 0x65, 0x72, 0x41, 0x6e, 0x64, 0x31, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, + 0x37, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x08, + 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x0a, 0x0a, 0x01, 0x6f, 0x12, 0x05, 0xba, + 0x48, 0x02, 0x08, 0x01, 0x22, 0x73, 0x0a, 0x10, 0x4f, 0x6e, 0x65, 0x6f, 0x66, 0x49, 0x67, 0x6e, + 0x6f, 0x72, 0x65, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x1c, 0x0a, 0x01, 0x78, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x0c, 0xba, 0x48, 0x09, 0xd0, 0x01, 0x01, 0x72, 0x04, 0x10, 0x03, 0x18, + 0x05, 0x48, 0x00, 0x52, 0x01, 0x78, 0x12, 0x1c, 0x0a, 0x01, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0c, 0x42, 0x0c, 0xba, 0x48, 0x09, 0xd0, 0x01, 0x01, 0x7a, 0x04, 0x10, 0x03, 0x18, 0x05, 0x48, + 0x00, 0x52, 0x01, 0x79, 0x12, 0x1e, 0x0a, 0x01, 0x7a, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x42, + 0x0e, 0xba, 0x48, 0x0b, 0xd0, 0x01, 0x01, 0x1a, 0x06, 0x18, 0x80, 0x01, 0x28, 0x80, 0x02, 0x48, + 0x00, 0x52, 0x01, 0x7a, 0x42, 0x03, 0x0a, 0x01, 0x6f, 0x42, 0xa2, 0x02, 0x0a, 0x22, 0x63, 0x6f, + 0x6d, 0x2e, 0x62, 0x75, 0x66, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x63, + 0x6f, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x73, 0x65, 0x73, + 0x42, 0x0b, 0x4f, 0x6e, 0x65, 0x6f, 0x66, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, + 0x53, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x75, 0x66, 0x62, + 0x75, 0x69, 0x6c, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x65, 0x2f, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x62, 0x75, 0x66, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x65, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x2f, 0x63, + 0x61, 0x73, 0x65, 0x73, 0xa2, 0x02, 0x04, 0x42, 0x56, 0x43, 0x43, 0xaa, 0x02, 0x1e, 0x42, 0x75, + 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x6f, + 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x43, 0x61, 0x73, 0x65, 0x73, 0xca, 0x02, 0x1e, 0x42, + 0x75, 0x66, 0x5c, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x5c, 0x43, 0x6f, 0x6e, 0x66, + 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x5c, 0x43, 0x61, 0x73, 0x65, 0x73, 0xe2, 0x02, 0x2a, + 0x42, 0x75, 0x66, 0x5c, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x5c, 0x43, 0x6f, 0x6e, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x5c, 0x43, 0x61, 0x73, 0x65, 0x73, 0x5c, 0x47, + 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x21, 0x42, 0x75, 0x66, + 0x3a, 0x3a, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x3a, 0x43, 0x6f, 0x6e, 0x66, + 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x3a, 0x3a, 0x43, 0x61, 0x73, 0x65, 0x73, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -538,19 +559,21 @@ func file_buf_validate_conformance_cases_oneofs_proto_rawDescGZIP() []byte { var file_buf_validate_conformance_cases_oneofs_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_buf_validate_conformance_cases_oneofs_proto_goTypes = []interface{}{ - (*TestOneofMsg)(nil), // 0: buf.validate.conformance.cases.TestOneofMsg - (*OneofNone)(nil), // 1: buf.validate.conformance.cases.OneofNone - (*Oneof)(nil), // 2: buf.validate.conformance.cases.Oneof - (*OneofRequired)(nil), // 3: buf.validate.conformance.cases.OneofRequired - (*OneofIgnoreEmpty)(nil), // 4: buf.validate.conformance.cases.OneofIgnoreEmpty + (*TestOneofMsg)(nil), // 0: buf.validate.conformance.cases.TestOneofMsg + (*OneofNone)(nil), // 1: buf.validate.conformance.cases.OneofNone + (*Oneof)(nil), // 2: buf.validate.conformance.cases.Oneof + (*OneofRequired)(nil), // 3: buf.validate.conformance.cases.OneofRequired + (*OneofIgnoreEmpty)(nil), // 4: buf.validate.conformance.cases.OneofIgnoreEmpty + (*durationpb.Duration)(nil), // 5: google.protobuf.Duration } var file_buf_validate_conformance_cases_oneofs_proto_depIdxs = []int32{ 0, // 0: buf.validate.conformance.cases.Oneof.z:type_name -> buf.validate.conformance.cases.TestOneofMsg - 1, // [1:1] is the sub-list for method output_type - 1, // [1:1] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name + 5, // 1: buf.validate.conformance.cases.OneofRequired.duration:type_name -> google.protobuf.Duration + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name } func init() { file_buf_validate_conformance_cases_oneofs_proto_init() } @@ -634,6 +657,7 @@ func file_buf_validate_conformance_cases_oneofs_proto_init() { (*OneofRequired_Y)(nil), (*OneofRequired_NameWithUnderscores)(nil), (*OneofRequired_UnderAnd_1Number)(nil), + (*OneofRequired_Duration)(nil), } file_buf_validate_conformance_cases_oneofs_proto_msgTypes[4].OneofWrappers = []interface{}{ (*OneofIgnoreEmpty_X)(nil), diff --git a/tools/internal/gen/buf/validate/validate.pb.go b/tools/internal/gen/buf/validate/validate.pb.go index 86822750..f291f310 100644 --- a/tools/internal/gen/buf/validate/validate.pb.go +++ b/tools/internal/gen/buf/validate/validate.pb.go @@ -177,16 +177,16 @@ func (x *MessageConstraints) GetCel() []*Constraint { // The `OneofConstraints` message type enables you to manage constraints for // oneof fields in your protobuf messages. Use the `required` constraint to ensure -// that exactly one of the fields within a oneof is set; validation will fail -// if none of the fields in the oneof are set: +// that exactly one of the fields within a oneof is not empty; validation will fail +// if all of the fields in the oneof are empty: type OneofConstraints struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // `required` is an optional boolean attribute that ensures that - // exactly one of the field options in a oneof is set; validation fails if - // no fields in the oneof are set. + // exactly one of the field options in a oneof is not empty; validation fails if + // all fields in the oneof are empty. // // ```proto // diff --git a/tools/protovalidate-conformance/internal/cases/cases_oneof.go b/tools/protovalidate-conformance/internal/cases/cases_oneof.go index 92ca240d..cd91f826 100644 --- a/tools/protovalidate-conformance/internal/cases/cases_oneof.go +++ b/tools/protovalidate-conformance/internal/cases/cases_oneof.go @@ -19,6 +19,7 @@ import ( "github.com/bufbuild/protovalidate/tools/internal/gen/buf/validate/conformance/cases" "github.com/bufbuild/protovalidate/tools/protovalidate-conformance/internal/results" "github.com/bufbuild/protovalidate/tools/protovalidate-conformance/internal/suites" + "google.golang.org/protobuf/types/known/durationpb" ) func oneofSuite() suites.Suite { @@ -59,10 +60,22 @@ func oneofSuite() suites.Suite { Message: &cases.Oneof{O: &cases.Oneof_Z{Z: &cases.TestOneofMsg{}}}, Expected: results.Violations(&validate.Violation{FieldPath: "z.val", ConstraintId: "bool.const"}), }, - "required/valid": { + "required/valid_scalar": { + Message: &cases.OneofRequired{O: &cases.OneofRequired_X{X: "foo"}}, + Expected: results.Success(true), + }, + "required/empty_scalar": { Message: &cases.OneofRequired{O: &cases.OneofRequired_X{X: ""}}, + Expected: results.Violations(&validate.Violation{FieldPath: "o", ConstraintId: "required"}), + }, + "required/valid_message": { + Message: &cases.OneofRequired{O: &cases.OneofRequired_Duration{Duration: &durationpb.Duration{}}}, Expected: results.Success(true), }, + "required/empty_message": { + Message: &cases.OneofRequired{O: &cases.OneofRequired_Duration{Duration: nil}}, + Expected: results.Violations(&validate.Violation{FieldPath: "o", ConstraintId: "required"}), + }, "required/invalid": { Message: &cases.OneofRequired{}, Expected: results.Violations(&validate.Violation{FieldPath: "o", ConstraintId: "required"}),