diff --git a/codec/v2/Makefile b/codec/v2/Makefile new file mode 100644 index 000000000000..b8205e21f424 --- /dev/null +++ b/codec/v2/Makefile @@ -0,0 +1,2 @@ +codegen: + (cd internal; buf generate) \ No newline at end of file diff --git a/codec/v2/go.mod b/codec/v2/go.mod new file mode 100644 index 000000000000..6cc6a0a77276 --- /dev/null +++ b/codec/v2/go.mod @@ -0,0 +1,11 @@ +module cosmossdk.io/codec/v2 + +go 1.18 + +require ( + golang.org/x/exp v0.0.0-20220907003533-145caa8ea1d0 + google.golang.org/protobuf v1.28.1 + gotest.tools/v3 v3.3.0 +) + +require github.com/google/go-cmp v0.5.8 // indirect diff --git a/codec/v2/go.sum b/codec/v2/go.sum new file mode 100644 index 000000000000..9587fa16c009 --- /dev/null +++ b/codec/v2/go.sum @@ -0,0 +1,35 @@ +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20220907003533-145caa8ea1d0 h1:17k44ji3KFYG94XS5QEFC8pyuOlMh3IoR+vkmTZmJJs= +golang.org/x/exp v0.0.0-20220907003533-145caa8ea1d0/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/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.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo= +gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A= diff --git a/codec/v2/internal/buf.gen.yaml b/codec/v2/internal/buf.gen.yaml new file mode 100644 index 000000000000..6500dd6c4891 --- /dev/null +++ b/codec/v2/internal/buf.gen.yaml @@ -0,0 +1,9 @@ +version: v1 +managed: + enabled: true + go_package_prefix: + default: cosmossdk.io/codec/v2/internal +plugins: + - name: go + out: . + opt: paths=source_relative diff --git a/codec/v2/internal/buf.yaml b/codec/v2/internal/buf.yaml new file mode 100644 index 000000000000..ac1df238ebec --- /dev/null +++ b/codec/v2/internal/buf.yaml @@ -0,0 +1,9 @@ +version: v1 +lint: + use: + - DEFAULT + except: + - PACKAGE_VERSION_SUFFIX +breaking: + ignore: + - testpb diff --git a/codec/v2/internal/testpb/test.pb.go b/codec/v2/internal/testpb/test.pb.go new file mode 100644 index 000000000000..7442c3776c1c --- /dev/null +++ b/codec/v2/internal/testpb/test.pb.go @@ -0,0 +1,763 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc (unknown) +// source: testpb/test.proto + +package testpb + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + anypb "google.golang.org/protobuf/types/known/anypb" + durationpb "google.golang.org/protobuf/types/known/durationpb" + emptypb "google.golang.org/protobuf/types/known/emptypb" + fieldmaskpb "google.golang.org/protobuf/types/known/fieldmaskpb" + structpb "google.golang.org/protobuf/types/known/structpb" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type AnEnum int32 + +const ( + AnEnum_UNDEFINED AnEnum = 0 + AnEnum_ONE AnEnum = 1 + AnEnum_TWO AnEnum = 2 +) + +// Enum value maps for AnEnum. +var ( + AnEnum_name = map[int32]string{ + 0: "UNDEFINED", + 1: "ONE", + 2: "TWO", + } + AnEnum_value = map[string]int32{ + "UNDEFINED": 0, + "ONE": 1, + "TWO": 2, + } +) + +func (x AnEnum) Enum() *AnEnum { + p := new(AnEnum) + *p = x + return p +} + +func (x AnEnum) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (AnEnum) Descriptor() protoreflect.EnumDescriptor { + return file_testpb_test_proto_enumTypes[0].Descriptor() +} + +func (AnEnum) Type() protoreflect.EnumType { + return &file_testpb_test_proto_enumTypes[0] +} + +func (x AnEnum) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use AnEnum.Descriptor instead. +func (AnEnum) EnumDescriptor() ([]byte, []int) { + return file_testpb_test_proto_rawDescGZIP(), []int{0} +} + +type ABitOfEverything struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // message + // Generates JSON objects. Message field names are mapped to lowerCamelCase and become JSON object keys. If the json_name field option is specified, the specified value will be used as the key instead. Parsers accept both the lowerCamelCase name (or the one specified by the json_name option) and the original proto field name. null is an accepted value for all field types and treated as the default value of the corresponding field type. + Message *NestedMessage `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + // enum + // The name of the enum value as specified in proto is used. Parsers accept both enum names and integer values. + Enum AnEnum `protobuf:"varint,2,opt,name=enum,proto3,enum=testpb.AnEnum" json:"enum,omitempty"` + // map + // All keys are converted to strings. + StrMap map[string]string `protobuf:"bytes,3,rep,name=str_map,json=strMap,proto3" json:"str_map,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Int32Map map[int32]string `protobuf:"bytes,4,rep,name=int32_map,json=int32Map,proto3" json:"int32_map,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + BoolMap map[bool]string `protobuf:"bytes,5,rep,name=bool_map,json=boolMap,proto3" json:"bool_map,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // repeated + Repeated []int32 `protobuf:"varint,6,rep,packed,name=repeated,proto3" json:"repeated,omitempty"` + Str string `protobuf:"bytes,7,opt,name=str,proto3" json:"str,omitempty"` + Bool bool `protobuf:"varint,8,opt,name=bool,proto3" json:"bool,omitempty"` + Bytes []byte `protobuf:"bytes,9,opt,name=bytes,proto3" json:"bytes,omitempty"` + I32 int32 `protobuf:"varint,10,opt,name=i32,proto3" json:"i32,omitempty"` + F32 uint32 `protobuf:"fixed32,11,opt,name=f32,proto3" json:"f32,omitempty"` + U32 uint32 `protobuf:"varint,12,opt,name=u32,proto3" json:"u32,omitempty"` + Si32 int32 `protobuf:"zigzag32,13,opt,name=si32,proto3" json:"si32,omitempty"` + Sf32 int32 `protobuf:"fixed32,14,opt,name=sf32,proto3" json:"sf32,omitempty"` + I64 int64 `protobuf:"varint,15,opt,name=i64,proto3" json:"i64,omitempty"` + F64 uint64 `protobuf:"fixed64,16,opt,name=f64,proto3" json:"f64,omitempty"` + U64 uint64 `protobuf:"varint,17,opt,name=u64,proto3" json:"u64,omitempty"` + Si64 int64 `protobuf:"zigzag64,18,opt,name=si64,proto3" json:"si64,omitempty"` + Sf64 int64 `protobuf:"fixed64,19,opt,name=sf64,proto3" json:"sf64,omitempty"` + Float float32 `protobuf:"fixed32,20,opt,name=float,proto3" json:"float,omitempty"` + Double float64 `protobuf:"fixed64,21,opt,name=double,proto3" json:"double,omitempty"` + Any *anypb.Any `protobuf:"bytes,22,opt,name=any,proto3" json:"any,omitempty"` + Timestamp *timestamppb.Timestamp `protobuf:"bytes,23,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Duration *durationpb.Duration `protobuf:"bytes,24,opt,name=duration,proto3" json:"duration,omitempty"` + Struct *structpb.Struct `protobuf:"bytes,25,opt,name=struct,proto3" json:"struct,omitempty"` + BoolValue *wrapperspb.BoolValue `protobuf:"bytes,26,opt,name=bool_value,json=boolValue,proto3" json:"bool_value,omitempty"` + BytesValue *wrapperspb.BytesValue `protobuf:"bytes,27,opt,name=bytes_value,json=bytesValue,proto3" json:"bytes_value,omitempty"` + DoubleValue *wrapperspb.DoubleValue `protobuf:"bytes,28,opt,name=double_value,json=doubleValue,proto3" json:"double_value,omitempty"` + FloatValue *wrapperspb.FloatValue `protobuf:"bytes,29,opt,name=float_value,json=floatValue,proto3" json:"float_value,omitempty"` + Int32Value *wrapperspb.Int32Value `protobuf:"bytes,30,opt,name=int32_value,json=int32Value,proto3" json:"int32_value,omitempty"` + Int64Value *wrapperspb.Int64Value `protobuf:"bytes,31,opt,name=int64_value,json=int64Value,proto3" json:"int64_value,omitempty"` + StringValue *wrapperspb.StringValue `protobuf:"bytes,32,opt,name=string_value,json=stringValue,proto3" json:"string_value,omitempty"` + Uint32Value *wrapperspb.UInt32Value `protobuf:"bytes,33,opt,name=uint32_value,json=uint32Value,proto3" json:"uint32_value,omitempty"` + Uint64Value *wrapperspb.UInt64Value `protobuf:"bytes,34,opt,name=uint64_value,json=uint64Value,proto3" json:"uint64_value,omitempty"` + FieldMask *fieldmaskpb.FieldMask `protobuf:"bytes,35,opt,name=field_mask,json=fieldMask,proto3" json:"field_mask,omitempty"` + ListValue *structpb.ListValue `protobuf:"bytes,36,opt,name=list_value,json=listValue,proto3" json:"list_value,omitempty"` + Value *structpb.Value `protobuf:"bytes,37,opt,name=value,proto3" json:"value,omitempty"` + NullValue structpb.NullValue `protobuf:"varint,38,opt,name=null_value,json=nullValue,proto3,enum=google.protobuf.NullValue" json:"null_value,omitempty"` + Empty *emptypb.Empty `protobuf:"bytes,39,opt,name=empty,proto3" json:"empty,omitempty"` +} + +func (x *ABitOfEverything) Reset() { + *x = ABitOfEverything{} + if protoimpl.UnsafeEnabled { + mi := &file_testpb_test_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ABitOfEverything) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ABitOfEverything) ProtoMessage() {} + +func (x *ABitOfEverything) ProtoReflect() protoreflect.Message { + mi := &file_testpb_test_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ABitOfEverything.ProtoReflect.Descriptor instead. +func (*ABitOfEverything) Descriptor() ([]byte, []int) { + return file_testpb_test_proto_rawDescGZIP(), []int{0} +} + +func (x *ABitOfEverything) GetMessage() *NestedMessage { + if x != nil { + return x.Message + } + return nil +} + +func (x *ABitOfEverything) GetEnum() AnEnum { + if x != nil { + return x.Enum + } + return AnEnum_UNDEFINED +} + +func (x *ABitOfEverything) GetStrMap() map[string]string { + if x != nil { + return x.StrMap + } + return nil +} + +func (x *ABitOfEverything) GetInt32Map() map[int32]string { + if x != nil { + return x.Int32Map + } + return nil +} + +func (x *ABitOfEverything) GetBoolMap() map[bool]string { + if x != nil { + return x.BoolMap + } + return nil +} + +func (x *ABitOfEverything) GetRepeated() []int32 { + if x != nil { + return x.Repeated + } + return nil +} + +func (x *ABitOfEverything) GetStr() string { + if x != nil { + return x.Str + } + return "" +} + +func (x *ABitOfEverything) GetBool() bool { + if x != nil { + return x.Bool + } + return false +} + +func (x *ABitOfEverything) GetBytes() []byte { + if x != nil { + return x.Bytes + } + return nil +} + +func (x *ABitOfEverything) GetI32() int32 { + if x != nil { + return x.I32 + } + return 0 +} + +func (x *ABitOfEverything) GetF32() uint32 { + if x != nil { + return x.F32 + } + return 0 +} + +func (x *ABitOfEverything) GetU32() uint32 { + if x != nil { + return x.U32 + } + return 0 +} + +func (x *ABitOfEverything) GetSi32() int32 { + if x != nil { + return x.Si32 + } + return 0 +} + +func (x *ABitOfEverything) GetSf32() int32 { + if x != nil { + return x.Sf32 + } + return 0 +} + +func (x *ABitOfEverything) GetI64() int64 { + if x != nil { + return x.I64 + } + return 0 +} + +func (x *ABitOfEverything) GetF64() uint64 { + if x != nil { + return x.F64 + } + return 0 +} + +func (x *ABitOfEverything) GetU64() uint64 { + if x != nil { + return x.U64 + } + return 0 +} + +func (x *ABitOfEverything) GetSi64() int64 { + if x != nil { + return x.Si64 + } + return 0 +} + +func (x *ABitOfEverything) GetSf64() int64 { + if x != nil { + return x.Sf64 + } + return 0 +} + +func (x *ABitOfEverything) GetFloat() float32 { + if x != nil { + return x.Float + } + return 0 +} + +func (x *ABitOfEverything) GetDouble() float64 { + if x != nil { + return x.Double + } + return 0 +} + +func (x *ABitOfEverything) GetAny() *anypb.Any { + if x != nil { + return x.Any + } + return nil +} + +func (x *ABitOfEverything) GetTimestamp() *timestamppb.Timestamp { + if x != nil { + return x.Timestamp + } + return nil +} + +func (x *ABitOfEverything) GetDuration() *durationpb.Duration { + if x != nil { + return x.Duration + } + return nil +} + +func (x *ABitOfEverything) GetStruct() *structpb.Struct { + if x != nil { + return x.Struct + } + return nil +} + +func (x *ABitOfEverything) GetBoolValue() *wrapperspb.BoolValue { + if x != nil { + return x.BoolValue + } + return nil +} + +func (x *ABitOfEverything) GetBytesValue() *wrapperspb.BytesValue { + if x != nil { + return x.BytesValue + } + return nil +} + +func (x *ABitOfEverything) GetDoubleValue() *wrapperspb.DoubleValue { + if x != nil { + return x.DoubleValue + } + return nil +} + +func (x *ABitOfEverything) GetFloatValue() *wrapperspb.FloatValue { + if x != nil { + return x.FloatValue + } + return nil +} + +func (x *ABitOfEverything) GetInt32Value() *wrapperspb.Int32Value { + if x != nil { + return x.Int32Value + } + return nil +} + +func (x *ABitOfEverything) GetInt64Value() *wrapperspb.Int64Value { + if x != nil { + return x.Int64Value + } + return nil +} + +func (x *ABitOfEverything) GetStringValue() *wrapperspb.StringValue { + if x != nil { + return x.StringValue + } + return nil +} + +func (x *ABitOfEverything) GetUint32Value() *wrapperspb.UInt32Value { + if x != nil { + return x.Uint32Value + } + return nil +} + +func (x *ABitOfEverything) GetUint64Value() *wrapperspb.UInt64Value { + if x != nil { + return x.Uint64Value + } + return nil +} + +func (x *ABitOfEverything) GetFieldMask() *fieldmaskpb.FieldMask { + if x != nil { + return x.FieldMask + } + return nil +} + +func (x *ABitOfEverything) GetListValue() *structpb.ListValue { + if x != nil { + return x.ListValue + } + return nil +} + +func (x *ABitOfEverything) GetValue() *structpb.Value { + if x != nil { + return x.Value + } + return nil +} + +func (x *ABitOfEverything) GetNullValue() structpb.NullValue { + if x != nil { + return x.NullValue + } + return structpb.NullValue(0) +} + +func (x *ABitOfEverything) GetEmpty() *emptypb.Empty { + if x != nil { + return x.Empty + } + return nil +} + +type NestedMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Foo string `protobuf:"bytes,1,opt,name=foo,proto3" json:"foo,omitempty"` + Bar int32 `protobuf:"varint,2,opt,name=bar,proto3" json:"bar,omitempty"` +} + +func (x *NestedMessage) Reset() { + *x = NestedMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_testpb_test_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NestedMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NestedMessage) ProtoMessage() {} + +func (x *NestedMessage) ProtoReflect() protoreflect.Message { + mi := &file_testpb_test_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NestedMessage.ProtoReflect.Descriptor instead. +func (*NestedMessage) Descriptor() ([]byte, []int) { + return file_testpb_test_proto_rawDescGZIP(), []int{1} +} + +func (x *NestedMessage) GetFoo() string { + if x != nil { + return x.Foo + } + return "" +} + +func (x *NestedMessage) GetBar() int32 { + if x != nil { + return x.Bar + } + return 0 +} + +var File_testpb_test_proto protoreflect.FileDescriptor + +var file_testpb_test_proto_rawDesc = []byte{ + 0x0a, 0x11, 0x74, 0x65, 0x73, 0x74, 0x70, 0x62, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x74, 0x65, 0x73, 0x74, 0x70, 0x62, 0x1a, 0x19, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 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, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, + 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb1, 0x0e, 0x0a, 0x10, 0x41, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x45, + 0x76, 0x65, 0x72, 0x79, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x12, 0x2f, 0x0a, 0x07, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x65, 0x73, + 0x74, 0x70, 0x62, 0x2e, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x22, 0x0a, 0x04, 0x65, 0x6e, + 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, + 0x62, 0x2e, 0x41, 0x6e, 0x45, 0x6e, 0x75, 0x6d, 0x52, 0x04, 0x65, 0x6e, 0x75, 0x6d, 0x12, 0x3d, + 0x0a, 0x07, 0x73, 0x74, 0x72, 0x5f, 0x6d, 0x61, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x24, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x62, 0x2e, 0x41, 0x42, 0x69, 0x74, 0x4f, 0x66, 0x45, + 0x76, 0x65, 0x72, 0x79, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x2e, 0x53, 0x74, 0x72, 0x4d, 0x61, 0x70, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x73, 0x74, 0x72, 0x4d, 0x61, 0x70, 0x12, 0x43, 0x0a, + 0x09, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x6d, 0x61, 0x70, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x26, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x62, 0x2e, 0x41, 0x42, 0x69, 0x74, 0x4f, 0x66, + 0x45, 0x76, 0x65, 0x72, 0x79, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x2e, 0x49, 0x6e, 0x74, 0x33, 0x32, + 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x4d, + 0x61, 0x70, 0x12, 0x40, 0x0a, 0x08, 0x62, 0x6f, 0x6f, 0x6c, 0x5f, 0x6d, 0x61, 0x70, 0x18, 0x05, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x62, 0x2e, 0x41, 0x42, + 0x69, 0x74, 0x4f, 0x66, 0x45, 0x76, 0x65, 0x72, 0x79, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x2e, 0x42, + 0x6f, 0x6f, 0x6c, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x62, 0x6f, 0x6f, + 0x6c, 0x4d, 0x61, 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, + 0x18, 0x06, 0x20, 0x03, 0x28, 0x05, 0x52, 0x08, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, + 0x12, 0x10, 0x0a, 0x03, 0x73, 0x74, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x73, + 0x74, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x6f, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x04, 0x62, 0x6f, 0x6f, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x12, 0x10, 0x0a, 0x03, + 0x69, 0x33, 0x32, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x69, 0x33, 0x32, 0x12, 0x10, + 0x0a, 0x03, 0x66, 0x33, 0x32, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x07, 0x52, 0x03, 0x66, 0x33, 0x32, + 0x12, 0x10, 0x0a, 0x03, 0x75, 0x33, 0x32, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x75, + 0x33, 0x32, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x33, 0x32, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x11, + 0x52, 0x04, 0x73, 0x69, 0x33, 0x32, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x66, 0x33, 0x32, 0x18, 0x0e, + 0x20, 0x01, 0x28, 0x0f, 0x52, 0x04, 0x73, 0x66, 0x33, 0x32, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x36, + 0x34, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x69, 0x36, 0x34, 0x12, 0x10, 0x0a, 0x03, + 0x66, 0x36, 0x34, 0x18, 0x10, 0x20, 0x01, 0x28, 0x06, 0x52, 0x03, 0x66, 0x36, 0x34, 0x12, 0x10, + 0x0a, 0x03, 0x75, 0x36, 0x34, 0x18, 0x11, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x75, 0x36, 0x34, + 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x36, 0x34, 0x18, 0x12, 0x20, 0x01, 0x28, 0x12, 0x52, 0x04, + 0x73, 0x69, 0x36, 0x34, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x66, 0x36, 0x34, 0x18, 0x13, 0x20, 0x01, + 0x28, 0x10, 0x52, 0x04, 0x73, 0x66, 0x36, 0x34, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x6f, 0x61, + 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, 0x02, 0x52, 0x05, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x12, 0x16, + 0x0a, 0x06, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x18, 0x15, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, + 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x12, 0x26, 0x0a, 0x03, 0x61, 0x6e, 0x79, 0x18, 0x16, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x03, 0x61, 0x6e, 0x79, 0x12, 0x38, + 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x17, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x35, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x18, 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, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x2f, 0x0a, 0x06, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x06, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, + 0x12, 0x39, 0x0a, 0x0a, 0x62, 0x6f, 0x6f, 0x6c, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x1a, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x52, 0x09, 0x62, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3c, 0x0a, 0x0b, 0x62, + 0x79, 0x74, 0x65, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x42, 0x79, 0x74, 0x65, 0x73, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0a, 0x62, + 0x79, 0x74, 0x65, 0x73, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3f, 0x0a, 0x0c, 0x64, 0x6f, 0x75, + 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x44, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0b, 0x64, + 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3c, 0x0a, 0x0b, 0x66, 0x6c, + 0x6f, 0x61, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0a, 0x66, 0x6c, + 0x6f, 0x61, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3c, 0x0a, 0x0b, 0x69, 0x6e, 0x74, 0x33, + 0x32, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0a, 0x69, 0x6e, 0x74, 0x33, + 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3c, 0x0a, 0x0b, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, + 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0a, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3f, 0x0a, 0x0c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x20, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, + 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0b, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3f, 0x0a, 0x0c, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x21, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, + 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0b, 0x75, 0x69, 0x6e, 0x74, 0x33, + 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3f, 0x0a, 0x0c, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, + 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x22, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, + 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0b, 0x75, 0x69, 0x6e, 0x74, + 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, + 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x23, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, + 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x09, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, + 0x73, 0x6b, 0x12, 0x39, 0x0a, 0x0a, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x24, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x52, 0x09, 0x6c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2c, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x25, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x6e, + 0x75, 0x6c, 0x6c, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x26, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x4e, 0x75, 0x6c, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x09, 0x6e, 0x75, 0x6c, + 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2c, 0x0a, 0x05, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x18, + 0x27, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x05, 0x65, + 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x39, 0x0a, 0x0b, 0x53, 0x74, 0x72, 0x4d, 0x61, 0x70, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, + 0x3b, 0x0a, 0x0d, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3a, 0x0a, 0x0c, + 0x42, 0x6f, 0x6f, 0x6c, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x33, 0x0a, 0x0d, 0x4e, 0x65, 0x73, 0x74, + 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x6f, 0x6f, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x66, 0x6f, 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x62, + 0x61, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x62, 0x61, 0x72, 0x2a, 0x29, 0x0a, + 0x06, 0x41, 0x6e, 0x45, 0x6e, 0x75, 0x6d, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x4e, 0x44, 0x45, 0x46, + 0x49, 0x4e, 0x45, 0x44, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4f, 0x4e, 0x45, 0x10, 0x01, 0x12, + 0x07, 0x0a, 0x03, 0x54, 0x57, 0x4f, 0x10, 0x02, 0x42, 0x76, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x2e, + 0x74, 0x65, 0x73, 0x74, 0x70, 0x62, 0x42, 0x09, 0x54, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x50, 0x01, 0x5a, 0x25, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x73, 0x64, 0x6b, 0x2e, 0x69, + 0x6f, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x63, 0x2f, 0x76, 0x32, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x70, 0x62, 0xa2, 0x02, 0x03, 0x54, 0x58, 0x58, + 0xaa, 0x02, 0x06, 0x54, 0x65, 0x73, 0x74, 0x70, 0x62, 0xca, 0x02, 0x06, 0x54, 0x65, 0x73, 0x74, + 0x70, 0x62, 0xe2, 0x02, 0x12, 0x54, 0x65, 0x73, 0x74, 0x70, 0x62, 0x5c, 0x47, 0x50, 0x42, 0x4d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x06, 0x54, 0x65, 0x73, 0x74, 0x70, 0x62, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_testpb_test_proto_rawDescOnce sync.Once + file_testpb_test_proto_rawDescData = file_testpb_test_proto_rawDesc +) + +func file_testpb_test_proto_rawDescGZIP() []byte { + file_testpb_test_proto_rawDescOnce.Do(func() { + file_testpb_test_proto_rawDescData = protoimpl.X.CompressGZIP(file_testpb_test_proto_rawDescData) + }) + return file_testpb_test_proto_rawDescData +} + +var file_testpb_test_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_testpb_test_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_testpb_test_proto_goTypes = []interface{}{ + (AnEnum)(0), // 0: testpb.AnEnum + (*ABitOfEverything)(nil), // 1: testpb.ABitOfEverything + (*NestedMessage)(nil), // 2: testpb.NestedMessage + nil, // 3: testpb.ABitOfEverything.StrMapEntry + nil, // 4: testpb.ABitOfEverything.Int32MapEntry + nil, // 5: testpb.ABitOfEverything.BoolMapEntry + (*anypb.Any)(nil), // 6: google.protobuf.Any + (*timestamppb.Timestamp)(nil), // 7: google.protobuf.Timestamp + (*durationpb.Duration)(nil), // 8: google.protobuf.Duration + (*structpb.Struct)(nil), // 9: google.protobuf.Struct + (*wrapperspb.BoolValue)(nil), // 10: google.protobuf.BoolValue + (*wrapperspb.BytesValue)(nil), // 11: google.protobuf.BytesValue + (*wrapperspb.DoubleValue)(nil), // 12: google.protobuf.DoubleValue + (*wrapperspb.FloatValue)(nil), // 13: google.protobuf.FloatValue + (*wrapperspb.Int32Value)(nil), // 14: google.protobuf.Int32Value + (*wrapperspb.Int64Value)(nil), // 15: google.protobuf.Int64Value + (*wrapperspb.StringValue)(nil), // 16: google.protobuf.StringValue + (*wrapperspb.UInt32Value)(nil), // 17: google.protobuf.UInt32Value + (*wrapperspb.UInt64Value)(nil), // 18: google.protobuf.UInt64Value + (*fieldmaskpb.FieldMask)(nil), // 19: google.protobuf.FieldMask + (*structpb.ListValue)(nil), // 20: google.protobuf.ListValue + (*structpb.Value)(nil), // 21: google.protobuf.Value + (structpb.NullValue)(0), // 22: google.protobuf.NullValue + (*emptypb.Empty)(nil), // 23: google.protobuf.Empty +} +var file_testpb_test_proto_depIdxs = []int32{ + 2, // 0: testpb.ABitOfEverything.message:type_name -> testpb.NestedMessage + 0, // 1: testpb.ABitOfEverything.enum:type_name -> testpb.AnEnum + 3, // 2: testpb.ABitOfEverything.str_map:type_name -> testpb.ABitOfEverything.StrMapEntry + 4, // 3: testpb.ABitOfEverything.int32_map:type_name -> testpb.ABitOfEverything.Int32MapEntry + 5, // 4: testpb.ABitOfEverything.bool_map:type_name -> testpb.ABitOfEverything.BoolMapEntry + 6, // 5: testpb.ABitOfEverything.any:type_name -> google.protobuf.Any + 7, // 6: testpb.ABitOfEverything.timestamp:type_name -> google.protobuf.Timestamp + 8, // 7: testpb.ABitOfEverything.duration:type_name -> google.protobuf.Duration + 9, // 8: testpb.ABitOfEverything.struct:type_name -> google.protobuf.Struct + 10, // 9: testpb.ABitOfEverything.bool_value:type_name -> google.protobuf.BoolValue + 11, // 10: testpb.ABitOfEverything.bytes_value:type_name -> google.protobuf.BytesValue + 12, // 11: testpb.ABitOfEverything.double_value:type_name -> google.protobuf.DoubleValue + 13, // 12: testpb.ABitOfEverything.float_value:type_name -> google.protobuf.FloatValue + 14, // 13: testpb.ABitOfEverything.int32_value:type_name -> google.protobuf.Int32Value + 15, // 14: testpb.ABitOfEverything.int64_value:type_name -> google.protobuf.Int64Value + 16, // 15: testpb.ABitOfEverything.string_value:type_name -> google.protobuf.StringValue + 17, // 16: testpb.ABitOfEverything.uint32_value:type_name -> google.protobuf.UInt32Value + 18, // 17: testpb.ABitOfEverything.uint64_value:type_name -> google.protobuf.UInt64Value + 19, // 18: testpb.ABitOfEverything.field_mask:type_name -> google.protobuf.FieldMask + 20, // 19: testpb.ABitOfEverything.list_value:type_name -> google.protobuf.ListValue + 21, // 20: testpb.ABitOfEverything.value:type_name -> google.protobuf.Value + 22, // 21: testpb.ABitOfEverything.null_value:type_name -> google.protobuf.NullValue + 23, // 22: testpb.ABitOfEverything.empty:type_name -> google.protobuf.Empty + 23, // [23:23] is the sub-list for method output_type + 23, // [23:23] is the sub-list for method input_type + 23, // [23:23] is the sub-list for extension type_name + 23, // [23:23] is the sub-list for extension extendee + 0, // [0:23] is the sub-list for field type_name +} + +func init() { file_testpb_test_proto_init() } +func file_testpb_test_proto_init() { + if File_testpb_test_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_testpb_test_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ABitOfEverything); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_testpb_test_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NestedMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_testpb_test_proto_rawDesc, + NumEnums: 1, + NumMessages: 5, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_testpb_test_proto_goTypes, + DependencyIndexes: file_testpb_test_proto_depIdxs, + EnumInfos: file_testpb_test_proto_enumTypes, + MessageInfos: file_testpb_test_proto_msgTypes, + }.Build() + File_testpb_test_proto = out.File + file_testpb_test_proto_rawDesc = nil + file_testpb_test_proto_goTypes = nil + file_testpb_test_proto_depIdxs = nil +} diff --git a/codec/v2/internal/testpb/test.proto b/codec/v2/internal/testpb/test.proto new file mode 100644 index 000000000000..95e31f6967ed --- /dev/null +++ b/codec/v2/internal/testpb/test.proto @@ -0,0 +1,90 @@ +syntax = "proto3"; + +package testpb; + +import "google/protobuf/any.proto"; +import "google/protobuf/timestamp.proto"; +import "google/protobuf/duration.proto"; +import "google/protobuf/struct.proto"; +import "google/protobuf/wrappers.proto"; +import "google/protobuf/field_mask.proto"; +import "google/protobuf/empty.proto"; + +message ABitOfEverything { + // message + // Generates JSON objects. Message field names are mapped to lowerCamelCase and become JSON object keys. If the json_name field option is specified, the specified value will be used as the key instead. Parsers accept both the lowerCamelCase name (or the one specified by the json_name option) and the original proto field name. null is an accepted value for all field types and treated as the default value of the corresponding field type. + NestedMessage message = 1; + + // enum + // The name of the enum value as specified in proto is used. Parsers accept both enum names and integer values. + AnEnum enum = 2; + + // map + // All keys are converted to strings. + map str_map = 3; + map int32_map = 4; + map bool_map = 5; + + // repeated + repeated int32 repeated = 6; + + string str = 7; + + bool bool = 8; + + bytes bytes = 9; + + int32 i32 = 10; + fixed32 f32 = 11; + uint32 u32 = 12; + sint32 si32 = 13; + sfixed32 sf32 = 14; + + int64 i64 = 15; + fixed64 f64 = 16; + uint64 u64 = 17; + sint64 si64 = 18; + sfixed64 sf64 = 19; + + float float = 20; + double double = 21; + + google.protobuf.Any any = 22; + + google.protobuf.Timestamp timestamp = 23; + + google.protobuf.Duration duration = 24; + + google.protobuf.Struct struct = 25; + + google.protobuf.BoolValue bool_value = 26; + google.protobuf.BytesValue bytes_value = 27; + google.protobuf.DoubleValue double_value = 28; + google.protobuf.FloatValue float_value = 29; + google.protobuf.Int32Value int32_value = 30; + google.protobuf.Int64Value int64_value = 31; + google.protobuf.StringValue string_value = 32; + google.protobuf.UInt32Value uint32_value = 33; + google.protobuf.UInt64Value uint64_value = 34; + + google.protobuf.FieldMask field_mask = 35; + + google.protobuf.ListValue list_value = 36; + + google.protobuf.Value value = 37; + + google.protobuf.NullValue null_value = 38; + + google.protobuf.Empty empty = 39; +} + +message NestedMessage { + string foo = 1; + int32 bar = 2; +} + +enum AnEnum { + UNDEFINED = 0; + ONE = 1; + TWO = 2; +} \ No newline at end of file diff --git a/codec/v2/stablejson/any.go b/codec/v2/stablejson/any.go new file mode 100644 index 000000000000..284b5013ca7e --- /dev/null +++ b/codec/v2/stablejson/any.go @@ -0,0 +1,64 @@ +package stablejson + +import ( + "fmt" + "io" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/reflect/protoregistry" +) + +const ( + typeUrlName protoreflect.Name = "type_url" + valueName protoreflect.Name = "value" +) + +func (opts MarshalOptions) marshalAny(message protoreflect.Message, writer io.Writer) error { + fields := message.Descriptor().Fields() + typeUrlField := fields.ByName(typeUrlName) + if typeUrlField == nil { + return fmt.Errorf("expected type_url field") + } + + _, err := writer.Write([]byte("{")) + if err != nil { + return err + } + + typeUrl := message.Get(typeUrlField).String() + resolver := opts.Resolver + if resolver == nil { + resolver = protoregistry.GlobalTypes + } + typ, err := resolver.FindMessageByURL(typeUrl) + if err != nil { + return err + } + + _, err = fmt.Fprintf(writer, `"@type_url":%q`, typeUrl) + if err != nil { + return err + } + + valueField := fields.ByName(valueName) + if valueField == nil { + return fmt.Errorf("expected value field") + } + + valueBz := message.Get(valueField).Bytes() + + valueMsg := typ.New() + err = proto.Unmarshal(valueBz, valueMsg.Interface()) + if err != nil { + return err + } + + err = opts.marshalMessageFields(valueMsg, writer, false) + if err != nil { + return err + } + + _, err = writer.Write([]byte("}")) + return err +} diff --git a/codec/v2/stablejson/duration.go b/codec/v2/stablejson/duration.go new file mode 100644 index 000000000000..bfdadb4de5b2 --- /dev/null +++ b/codec/v2/stablejson/duration.go @@ -0,0 +1,13 @@ +package stablejson + +import ( + io "io" + + "google.golang.org/protobuf/reflect/protoreflect" +) + +const () + +func marshalDuration(writer io.Writer, message protoreflect.Message) error { + return nil +} diff --git a/codec/v2/stablejson/enum.go b/codec/v2/stablejson/enum.go new file mode 100644 index 000000000000..98fb653f5357 --- /dev/null +++ b/codec/v2/stablejson/enum.go @@ -0,0 +1,24 @@ +package stablejson + +import ( + "fmt" + "io" + + "google.golang.org/protobuf/reflect/protoreflect" +) + +func (opts MarshalOptions) marshalEnum(fieldDescriptor protoreflect.FieldDescriptor, value protoreflect.EnumNumber, writer io.Writer) error { + enumDescriptor := fieldDescriptor.Enum() + if enumDescriptor == nil { + return fmt.Errorf("expected enum descriptor for %s", fieldDescriptor.FullName()) + } + + enumValueDescriptor := enumDescriptor.Values().ByNumber(value) + var err error + if enumValueDescriptor != nil { + _, err = fmt.Fprintf(writer, "%q", enumValueDescriptor.Name()) + } else { + _, err = fmt.Fprintf(writer, "%d", value) + } + return err +} diff --git a/codec/v2/stablejson/fieldmask.go b/codec/v2/stablejson/fieldmask.go new file mode 100644 index 000000000000..b2e54d580a8e --- /dev/null +++ b/codec/v2/stablejson/fieldmask.go @@ -0,0 +1,29 @@ +package stablejson + +import ( + "fmt" + io "io" + "strings" + + "google.golang.org/protobuf/reflect/protoreflect" +) + +const ( + pathsName protoreflect.Name = "paths" +) + +func marshalFieldMask(writer io.Writer, value protoreflect.Message) error { + field := value.Descriptor().Fields().ByName(pathsName) + if field == nil { + return fmt.Errorf("expected to find field %s", pathsName) + } + + paths := value.Get(field).List() + n := paths.Len() + strs := make([]string, n) + for i := 0; i < n; i++ { + strs[i] = paths.Get(i).String() + } + _, _ = fmt.Fprintf(writer, "%q", strings.Join(strs, ",")) + return nil +} diff --git a/codec/v2/stablejson/float.go b/codec/v2/stablejson/float.go new file mode 100644 index 000000000000..53a0a7288880 --- /dev/null +++ b/codec/v2/stablejson/float.go @@ -0,0 +1,25 @@ +package stablejson + +import ( + "io" + "math" + "strconv" +) + +func marshalFloat(writer io.Writer, x float64) error { + // PROTO3 SPEC: + // JSON value will be a number or one of the special string values "NaN", "Infinity", and "-Infinity". + // Either numbers or strings are accepted. Exponent notation is also accepted. + // -0 is considered equivalent to 0. + var err error + if math.IsInf(x, -1) { + _, err = writer.Write([]byte("-Infinity")) + } else if math.IsInf(x, 1) { + _, err = writer.Write([]byte("Infinity")) + } else if math.IsNaN(x) { + _, err = writer.Write([]byte("NaN")) + } else { + _, err = writer.Write([]byte(strconv.FormatFloat(x, 'f', -1, 64))) + } + return err +} diff --git a/codec/v2/stablejson/list.go b/codec/v2/stablejson/list.go new file mode 100644 index 000000000000..78667e247a6d --- /dev/null +++ b/codec/v2/stablejson/list.go @@ -0,0 +1,31 @@ +package stablejson + +import ( + "io" + + "google.golang.org/protobuf/reflect/protoreflect" +) + +func (opts MarshalOptions) marshalList(fieldDescriptor protoreflect.FieldDescriptor, list protoreflect.List, writer io.Writer) error { + n := list.Len() + _, err := writer.Write([]byte("[")) + if err != nil { + return err + } + + first := true + for i := 0; i < n; i++ { + if !first { + _, err := writer.Write([]byte(",")) + if err != nil { + return err + } + } + first = false + + err = opts.marshalSingleValue(fieldDescriptor, list.Get(i), writer) + } + + _, err = writer.Write([]byte("]")) + return err +} diff --git a/codec/v2/stablejson/map.go b/codec/v2/stablejson/map.go new file mode 100644 index 000000000000..37c68697a1c3 --- /dev/null +++ b/codec/v2/stablejson/map.go @@ -0,0 +1,11 @@ +package stablejson + +import ( + "io" + + "google.golang.org/protobuf/reflect/protoreflect" +) + +func (opts MarshalOptions) marshalMap(descriptor protoreflect.FieldDescriptor, value protoreflect.Map, writer io.Writer) error { + return nil +} diff --git a/codec/v2/stablejson/marshal.go b/codec/v2/stablejson/marshal.go new file mode 100644 index 000000000000..71a1a70d1f17 --- /dev/null +++ b/codec/v2/stablejson/marshal.go @@ -0,0 +1,37 @@ +package stablejson + +import ( + "bytes" + "io" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoregistry" +) + +func Marshal(message proto.Message) ([]byte, error) { + return MarshalOptions{}.Marshal(message) +} + +type MarshalOptions struct { + // HexBytes specifies whether bytes fields should be marshaled as upper-case + // hex strings. If set to false, bytes fields will be encoded as standard + // base64 strings as specified by the official proto3 JSON mapping. + HexBytes bool + + // Resolver is used for looking up types when expanding google.protobuf.Any + // messages. If nil, this defaults to using protoregistry.GlobalTypes. + Resolver interface { + protoregistry.ExtensionTypeResolver + protoregistry.MessageTypeResolver + } +} + +func (opts MarshalOptions) Marshal(message proto.Message) ([]byte, error) { + buf := &bytes.Buffer{} + err := opts.MarshalTo(message, buf) + return buf.Bytes(), err +} + +func (opts MarshalOptions) MarshalTo(message proto.Message, writer io.Writer) error { + return opts.marshalMessage(message.ProtoReflect(), writer) +} diff --git a/codec/v2/stablejson/marshal_test.go b/codec/v2/stablejson/marshal_test.go new file mode 100644 index 000000000000..02eee6156afa --- /dev/null +++ b/codec/v2/stablejson/marshal_test.go @@ -0,0 +1,103 @@ +package stablejson_test + +import ( + "testing" + "time" + + "google.golang.org/protobuf/types/known/anypb" + "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/emptypb" + "google.golang.org/protobuf/types/known/fieldmaskpb" + "google.golang.org/protobuf/types/known/structpb" + "google.golang.org/protobuf/types/known/timestamppb" + "google.golang.org/protobuf/types/known/wrapperspb" + "gotest.tools/v3/assert" + + "cosmossdk.io/codec/v2/internal/testpb" + "cosmossdk.io/codec/v2/stablejson" +) + +func TestStableJSON(t *testing.T) { + a, err := anypb.New(&testpb.ABitOfEverything{ + I32: 10, + Str: "abc", + }) + assert.NilError(t, err) + msg := &testpb.ABitOfEverything{ + Message: &testpb.NestedMessage{ + Foo: "test", + Bar: 0, // this is the default value and should be omitted from output + }, + Enum: testpb.AnEnum_ONE, + StrMap: map[string]string{ + "foo": "abc", + "bar": "def", + }, + Int32Map: map[int32]string{ + -3: "xyz", + 0: "abc", + 10: "qrs", + }, + BoolMap: map[bool]string{ + true: "T", + false: "F", + }, + Repeated: []int32{3, -7, 2, 6, 4}, + Str: `abcxyz"foo"def`, + Bool: true, + Bytes: []byte{0, 1, 2, 3}, + I32: -15, + F32: 1001, + U32: 1200, + Si32: -376, + Sf32: -1000, + I64: 14578294827584932, + F64: 9572348124213523654, + U64: 4759492485, + Si64: -59268425823934, + Sf64: -659101379604211154, + Float: 1.0, + Double: 5235.2941, + Any: a, + Timestamp: timestamppb.New(time.Date(2022, 1, 1, 12, 31, 0, 0, time.UTC)), + Duration: durationpb.New(time.Second * 3000), + Struct: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "null": structpb.NewNullValue(), + "num": structpb.NewNumberValue(3.76), + "str": structpb.NewStringValue("abc"), + "bool": structpb.NewBoolValue(true), + "nested struct": structpb.NewStructValue(&structpb.Struct{Fields: map[string]*structpb.Value{ + "a": structpb.NewStringValue("abc"), + }}), + "struct list": structpb.NewListValue(&structpb.ListValue{Values: []*structpb.Value{ + structpb.NewStringValue("xyz"), + structpb.NewBoolValue(false), + structpb.NewNumberValue(-9), + }}), + }, + }, + BoolValue: &wrapperspb.BoolValue{Value: true}, + BytesValue: &wrapperspb.BytesValue{Value: []byte{0, 1, 2, 3}}, + DoubleValue: &wrapperspb.DoubleValue{Value: 1.324}, + FloatValue: &wrapperspb.FloatValue{Value: -1.0}, + Int32Value: &wrapperspb.Int32Value{Value: 10}, + Int64Value: &wrapperspb.Int64Value{Value: -376923457}, + StringValue: &wrapperspb.StringValue{Value: "gfedcba"}, + Uint32Value: &wrapperspb.UInt32Value{Value: 37492}, + Uint64Value: &wrapperspb.UInt64Value{Value: 1892409137358391}, + FieldMask: &fieldmaskpb.FieldMask{Paths: []string{"a.b", "a.c", "b"}}, + ListValue: &structpb.ListValue{Values: []*structpb.Value{ + structpb.NewNumberValue(1.1), + structpb.NewStringValue("qrs"), + }}, + Value: &structpb.Value{}, + NullValue: structpb.NullValue_NULL_VALUE, + Empty: &emptypb.Empty{}, + } + bz, err := stablejson.Marshal(msg) + assert.NilError(t, err) + assert.Equal(t, + `{"message":{"foo":"test"},"enum":"ONE","str_map":,"int32_map":,"bool_map":,"repeated":[3,-7,2,6,4],"str":"abcxyz\"foo\"def","bool":true,"bytes":"AAECAw==","i32":-15,"f32":1001,"u32":1200,"si32":-376,"sf32":-1000,"i64":"14578294827584932","f64":"9572348124213523654","u64":"4759492485","si64":"-59268425823934","sf64":"-659101379604211154","float":1,"double":5235.2941,"any":{"@type_url":"type.googleapis.com/testpb.ABitOfEverything","str":"abc","i32":10},"timestamp":,"duration":,"struct":{"bool":true,"nested struct":{"a":"abc"},"null":null,"num":3.76,"str":"abc","struct list":["xyz",false,-9]},"bool_value":true,"bytes_value":"AAECAw==","double_value":1.324,"float_value":-1,"int32_value":10,"int64_value":"-376923457","string_value":"gfedcba","uint32_value":37492,"uint64_value":"1892409137358391","field_mask":"a.b,a.c,b","list_value":[1.1,"qrs"],"value":,"empty":{}}`, + string(bz)) +} diff --git a/codec/v2/stablejson/message.go b/codec/v2/stablejson/message.go new file mode 100644 index 000000000000..85d117cfb4cd --- /dev/null +++ b/codec/v2/stablejson/message.go @@ -0,0 +1,105 @@ +package stablejson + +import ( + "fmt" + "io" + "sort" + + "google.golang.org/protobuf/reflect/protoreflect" +) + +func (opts MarshalOptions) marshalMessage(message protoreflect.Message, writer io.Writer) error { + switch message.Descriptor().FullName() { + case timestampFullName: + return marshalTimestamp(writer, message) + case durationFullName: + return marshalDuration(writer, message) + case structFullName: + return marshalStruct(writer, message) + case listValueFullName: + return marshalListValue(writer, message) + case valueFullName: + return marshalStructValue(writer, message) + case nullValueFullName: + _, err := writer.Write([]byte("null")) + return err + case boolValueFullName, int32ValueFullName, int64ValueFullName, uint32ValueFullName, uint64ValueFullName, + stringValueFullName, bytesValueFullName, floatValueFullName, doubleValueFullName: + return opts.marshalWrapper(writer, message) + case fieldMaskFullName: + return marshalFieldMask(writer, message) + case anyFullName: + return opts.marshalAny(message, writer) + } + + _, err := writer.Write([]byte("{")) + if err != nil { + return err + } + + err = opts.marshalMessageFields(message, writer, true) + if err != nil { + return err + } + + _, err = writer.Write([]byte("}")) + return err +} + +func (opts MarshalOptions) marshalMessageFields(message protoreflect.Message, writer io.Writer, first bool) error { + fields := message.Descriptor().Fields() + numFields := fields.Len() + allFields := make([]protoreflect.FieldDescriptor, numFields) + for i := 0; i < numFields; i++ { + allFields[i] = fields.Get(i) + } + sort.Slice(allFields, func(i, j int) bool { + return allFields[i].Number() < allFields[j].Number() + }) + + for _, field := range allFields { + if !message.Has(field) { + continue + } + + if !first { + _, err := writer.Write([]byte(",")) + if err != nil { + return err + } + } + first = false + + _, err := fmt.Fprintf(writer, "%q:", field.Name()) + if err != nil { + return err + } + + err = opts.MarshalFieldValue(field, message.Get(field), writer) + if err != nil { + return err + } + } + + return nil +} + +const ( + timestampFullName protoreflect.FullName = "google.protobuf.Timestamp" + durationFullName = "google.protobuf.Duration" + structFullName = "google.protobuf.Struct" + valueFullName = "google.protobuf.Value" + listValueFullName = "google.protobuf.ListValue" + nullValueFullName = "google.protobuf.NullValue" + boolValueFullName = "google.protobuf.BoolValue" + stringValueFullName = "google.protobuf.StringValue" + bytesValueFullName = "google.protobuf.BytesValue" + int32ValueFullName = "google.protobuf.Int32Value" + int64ValueFullName = "google.protobuf.Int64Value" + uint32ValueFullName = "google.protobuf.UInt32Value" + uint64ValueFullName = "google.protobuf.UInt64Value" + floatValueFullName = "google.protobuf.FloatValue" + doubleValueFullName = "google.protobuf.DoubleValue" + fieldMaskFullName = "google.protobuf.FieldMask" + anyFullName = "google.protobuf.Any" +) diff --git a/codec/v2/stablejson/struct.go b/codec/v2/stablejson/struct.go new file mode 100644 index 000000000000..73c14d6f25d1 --- /dev/null +++ b/codec/v2/stablejson/struct.go @@ -0,0 +1,117 @@ +package stablejson + +import ( + "fmt" + io "io" + "sort" + + "golang.org/x/exp/maps" + "google.golang.org/protobuf/reflect/protoreflect" +) + +const ( + fieldsField protoreflect.Name = "fields" + valuesField protoreflect.Name = "values" + kindOneOf protoreflect.Name = "kind" + nullValueField protoreflect.Name = "null_value" + numberValueField protoreflect.Name = "number_value" + stringValueField protoreflect.Name = "string_value" + boolValueField protoreflect.Name = "bool_value" + structValueField protoreflect.Name = "struct_value" + listValueField protoreflect.Name = "list_value" +) + +func marshalStruct(writer io.Writer, value protoreflect.Message) error { + field := value.Descriptor().Fields().ByName(fieldsField) + m1 := value.Get(field).Map() + + _, err := writer.Write([]byte("{")) + if err != nil { + return err + } + + m2 := map[string]protoreflect.Message{} + m1.Range(func(key protoreflect.MapKey, value protoreflect.Value) bool { + m2[key.String()] = value.Message() + return true + }) + + keys := maps.Keys(m2) + sort.Strings(keys) + first := true + for _, k := range keys { + if !first { + _, err := writer.Write([]byte(",")) + if err != nil { + return err + } + } + + first = false + _, _ = fmt.Fprintf(writer, "%q:", k) + + err := marshalStructValue(writer, m2[k]) + if err != nil { + return err + } + } + + _, err = writer.Write([]byte("}")) + return err +} + +func marshalListValue(writer io.Writer, value protoreflect.Message) error { + field := value.Descriptor().Fields().ByName(valuesField) + list := value.Get(field).List() + n := list.Len() + + _, err := writer.Write([]byte("[")) + if err != nil { + return err + } + + first := true + for i := 0; i < n; i++ { + if !first { + _, err = writer.Write([]byte(",")) + if err != nil { + return err + } + } + first = false + + err := marshalStructValue(writer, list.Get(i).Message()) + if err != nil { + return err + } + } + + _, err = writer.Write([]byte("]")) + return err +} + +func marshalStructValue(writer io.Writer, value protoreflect.Message) error { + field := value.WhichOneof(value.Descriptor().Oneofs().ByName(kindOneOf)) + if field == nil { + return nil + } + + var err error + switch field.Name() { + case nullValueField: + _, err = writer.Write([]byte("null")) + case numberValueField: + err = marshalFloat(writer, value.Get(field).Float()) + case stringValueField: + _, err = fmt.Fprintf(writer, "%q", value.Get(field).String()) + case boolValueField: + _, err = fmt.Fprintf(writer, "%t", value.Get(field).Bool()) + case structValueField: + return marshalStruct(writer, value.Get(field).Message()) + case listValueField: + return marshalListValue(writer, value.Get(field).Message()) + default: + return fmt.Errorf("unexpected field in google.protobuf.Value: %v", field) + } + return err +} diff --git a/codec/v2/stablejson/timestamp.go b/codec/v2/stablejson/timestamp.go new file mode 100644 index 000000000000..6c0a17d282d8 --- /dev/null +++ b/codec/v2/stablejson/timestamp.go @@ -0,0 +1,11 @@ +package stablejson + +import ( + io "io" + + "google.golang.org/protobuf/reflect/protoreflect" +) + +func marshalTimestamp(writer io.Writer, message protoreflect.Message) error { + return nil +} diff --git a/codec/v2/stablejson/value.go b/codec/v2/stablejson/value.go new file mode 100644 index 000000000000..c3f7ea563528 --- /dev/null +++ b/codec/v2/stablejson/value.go @@ -0,0 +1,72 @@ +package stablejson + +import ( + "encoding/base64" + "fmt" + "io" + + "google.golang.org/protobuf/reflect/protoreflect" +) + +func (opts MarshalOptions) MarshalFieldValue(fieldDescriptor protoreflect.FieldDescriptor, value protoreflect.Value, writer io.Writer) error { + if fieldDescriptor.IsList() { + return opts.marshalList(fieldDescriptor, value.List(), writer) + } else if fieldDescriptor.IsMap() { + return opts.marshalMap(fieldDescriptor, value.Map(), writer) + } else { + return opts.marshalSingleValue(fieldDescriptor, value, writer) + } +} + +// marshalSingleValue marshals values that are not list/map fields or that are elements of list map fields +func (opts MarshalOptions) marshalSingleValue(fieldDescriptor protoreflect.FieldDescriptor, value protoreflect.Value, writer io.Writer) error { + switch value := value.Interface().(type) { + case protoreflect.Message: + return opts.marshalMessage(value, writer) + case protoreflect.EnumNumber: + return opts.marshalEnum(fieldDescriptor, value, writer) + default: + return opts.marshalPrimitive(writer, value) + } +} + +func (opts MarshalOptions) marshalPrimitive(writer io.Writer, value interface{}) error { + var err error + switch value := value.(type) { + case string: + _, err = fmt.Fprintf(writer, "%q", value) + case []byte: + _, err = writer.Write([]byte(`"`)) + if err != nil { + return err + } + + if opts.HexBytes { + _, err = fmt.Fprintf(writer, "%X", value) + } else { + _, err = writer.Write([]byte(base64.StdEncoding.EncodeToString(value))) + } + if err != nil { + return err + } + + _, err = writer.Write([]byte(`"`)) + case bool: + _, err = fmt.Fprintf(writer, "%t", value) + case int32: + _, err = fmt.Fprintf(writer, "%d", value) + case uint32: + _, err = fmt.Fprintf(writer, "%d", value) + case int64: + _, err = fmt.Fprintf(writer, `"%d"`, value) // quoted + case uint64: + _, err = fmt.Fprintf(writer, `"%d"`, value) // quoted + case float32: + err = marshalFloat(writer, float64(value)) + case float64: + err = marshalFloat(writer, value) + default: + return fmt.Errorf("unexpected type %T", value) + } + return err +} diff --git a/codec/v2/stablejson/wrappers.go b/codec/v2/stablejson/wrappers.go new file mode 100644 index 000000000000..df12bbad6ecd --- /dev/null +++ b/codec/v2/stablejson/wrappers.go @@ -0,0 +1,16 @@ +package stablejson + +import ( + io "io" + + "google.golang.org/protobuf/reflect/protoreflect" +) + +const ( + valueField = "value" +) + +func (opts MarshalOptions) marshalWrapper(writer io.Writer, message protoreflect.Message) error { + value := message.Get(message.Descriptor().Fields().ByName(valueField)) + return opts.marshalPrimitive(writer, value.Interface()) +}