Skip to content

Commit

Permalink
types/known/structpb: add support for more types and json.Number
Browse files Browse the repository at this point in the history
Add support for additional primitive types such as int8, int16, uint8, uint16,
and the json.Number type in the conversion logic of the NewValue function.

This change ensures that the generated code can handle a wider range of Go types
when converting them to protobuf Value types, improving compatibility with JSON
data formats.

fixes golang/protobuf#1463

Change-Id: I6ea7b8f644f6c3dbe5e6c17e744be40c56846328
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/601775
Reviewed-by: Michael Stapelberg <stapelberg@google.com>
Reviewed-by: Lasse Folger <lassefolger@google.com>
Reviewed-by: Funda Secgin <fundasecgin32@gmail.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
  • Loading branch information
zishang520 authored and stapelberg committed Aug 7, 2024
1 parent 75479a3 commit b36f8e0
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 52 deletions.
1 change: 1 addition & 0 deletions cmd/protoc-gen-go/internal_gengo/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ var GenerateVersionMarkers = true
// Standard library dependencies.
const (
base64Package = protogen.GoImportPath("encoding/base64")
jsonPackage = protogen.GoImportPath("encoding/json")
mathPackage = protogen.GoImportPath("math")
reflectPackage = protogen.GoImportPath("reflect")
sortPackage = protogen.GoImportPath("sort")
Expand Down
41 changes: 28 additions & 13 deletions cmd/protoc-gen-go/internal_gengo/well_known_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -713,19 +713,20 @@ func genMessageKnownFunctions(g *protogen.GeneratedFile, f *fileInfo, m *message
case genid.Value_message_fullname:
g.P("// NewValue constructs a Value from a general-purpose Go interface.")
g.P("//")
g.P("// ╔════════════════════════╤════════════════════════════════════════════╗")
g.P("// ║ Go type │ Conversion ║")
g.P("// ╠════════════════════════╪════════════════════════════════════════════╣")
g.P("// ║ nil │ stored as NullValue ║")
g.P("// ║ bool │ stored as BoolValue ║")
g.P("// ║ int, int32, int64 │ stored as NumberValue ║")
g.P("// ║ uint, uint32, uint64 │ stored as NumberValue ║")
g.P("// ║ float32, float64 │ stored as NumberValue ║")
g.P("// ║ string │ stored as StringValue; must be valid UTF-8 ║")
g.P("// ║ []byte │ stored as StringValue; base64-encoded ║")
g.P("// ║ map[string]any │ stored as StructValue ║")
g.P("// ║ []any │ stored as ListValue ║")
g.P("// ╚════════════════════════╧════════════════════════════════════════════╝")
g.P("// ╔═══════════════════════════════════════╤════════════════════════════════════════════╗")
g.P("// ║ Go type │ Conversion ║")
g.P("// ╠═══════════════════════════════════════╪════════════════════════════════════════════╣")
g.P("// ║ nil │ stored as NullValue ║")
g.P("// ║ bool │ stored as BoolValue ║")
g.P("// ║ int, int8, int16, int32, int64 │ stored as NumberValue ║")
g.P("// ║ uint, uint8, uint16, uint32, uint64 │ stored as NumberValue ║")
g.P("// ║ float32, float64 │ stored as NumberValue ║")
g.P("// ║ json.Number │ stored as NumberValue ║")
g.P("// ║ string │ stored as StringValue; must be valid UTF-8 ║")
g.P("// ║ []byte │ stored as StringValue; base64-encoded ║")
g.P("// ║ map[string]any │ stored as StructValue ║")
g.P("// ║ []any │ stored as ListValue ║")
g.P("// ╚═══════════════════════════════════════╧════════════════════════════════════════════╝")
g.P("//")
g.P("// When converting an int64 or uint64 to a NumberValue, numeric precision loss")
g.P("// is possible since they are stored as a float64.")
Expand All @@ -737,12 +738,20 @@ func genMessageKnownFunctions(g *protogen.GeneratedFile, f *fileInfo, m *message
g.P(" return NewBoolValue(v), nil")
g.P(" case int:")
g.P(" return NewNumberValue(float64(v)), nil")
g.P(" case int8:")
g.P(" return NewNumberValue(float64(v)), nil")
g.P(" case int16:")
g.P(" return NewNumberValue(float64(v)), nil")
g.P(" case int32:")
g.P(" return NewNumberValue(float64(v)), nil")
g.P(" case int64:")
g.P(" return NewNumberValue(float64(v)), nil")
g.P(" case uint:")
g.P(" return NewNumberValue(float64(v)), nil")
g.P(" case uint8:")
g.P(" return NewNumberValue(float64(v)), nil")
g.P(" case uint16:")
g.P(" return NewNumberValue(float64(v)), nil")
g.P(" case uint32:")
g.P(" return NewNumberValue(float64(v)), nil")
g.P(" case uint64:")
Expand All @@ -751,6 +760,12 @@ func genMessageKnownFunctions(g *protogen.GeneratedFile, f *fileInfo, m *message
g.P(" return NewNumberValue(float64(v)), nil")
g.P(" case float64:")
g.P(" return NewNumberValue(float64(v)), nil")
g.P(" case ", jsonPackage.Ident("Number"), ":")
g.P(" n, err := v.Float64()")
g.P(" if err != nil {")
g.P(" return nil, ", protoimplPackage.Ident("X"), ".NewError(\"invalid number format %q, expected a float64: %v\", v, err)")
g.P(" }")
g.P(" return NewNumberValue(n), nil")
g.P(" case string:")
g.P(" if !", utf8Package.Ident("ValidString"), "(v) {")
g.P(" return nil, ", protoimplPackage.Ident("X"), ".NewError(\"invalid UTF-8 in string: %q\", v)")
Expand Down
42 changes: 29 additions & 13 deletions types/known/structpb/struct.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

115 changes: 89 additions & 26 deletions types/known/structpb/struct_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,34 +37,44 @@ func TestToStruct(t *testing.T) {
wantPB: new(spb.Struct),
}, {
in: map[string]any{
"nil": nil,
"bool": bool(false),
"int": int(-123),
"int32": int32(math.MinInt32),
"int64": int64(math.MinInt64),
"uint": uint(123),
"uint32": uint32(math.MaxInt32),
"uint64": uint64(math.MaxInt64),
"float32": float32(123.456),
"float64": float64(123.456),
"string": string("hello, world!"),
"bytes": []byte("\xde\xad\xbe\xef"),
"map": map[string]any{"k1": "v1", "k2": "v2"},
"slice": []any{"one", "two", "three"},
"nil": nil,
"bool": bool(false),
"int": int(-123),
"int8": int8(math.MinInt8),
"int16": int16(math.MinInt16),
"int32": int32(math.MinInt32),
"int64": int64(math.MinInt64),
"uint": uint(123),
"uint8": uint8(math.MaxInt8),
"uint16": uint16(math.MaxInt16),
"uint32": uint32(math.MaxInt32),
"uint64": uint64(math.MaxInt64),
"float32": float32(123.456),
"float64": float64(123.456),
"jsonNumber": json.Number("123.456"),
"string": string("hello, world!"),
"bytes": []byte("\xde\xad\xbe\xef"),
"map": map[string]any{"k1": "v1", "k2": "v2"},
"slice": []any{"one", "two", "three"},
},
wantPB: &spb.Struct{Fields: map[string]*spb.Value{
"nil": spb.NewNullValue(),
"bool": spb.NewBoolValue(false),
"int": spb.NewNumberValue(float64(-123)),
"int32": spb.NewNumberValue(float64(math.MinInt32)),
"int64": spb.NewNumberValue(float64(math.MinInt64)),
"uint": spb.NewNumberValue(float64(123)),
"uint32": spb.NewNumberValue(float64(math.MaxInt32)),
"uint64": spb.NewNumberValue(float64(math.MaxInt64)),
"float32": spb.NewNumberValue(float64(float32(123.456))),
"float64": spb.NewNumberValue(float64(float64(123.456))),
"string": spb.NewStringValue("hello, world!"),
"bytes": spb.NewStringValue("3q2+7w=="),
"nil": spb.NewNullValue(),
"bool": spb.NewBoolValue(false),
"int": spb.NewNumberValue(float64(-123)),
"int8": spb.NewNumberValue(float64(math.MinInt8)),
"int16": spb.NewNumberValue(float64(math.MinInt16)),
"int32": spb.NewNumberValue(float64(math.MinInt32)),
"int64": spb.NewNumberValue(float64(math.MinInt64)),
"uint": spb.NewNumberValue(float64(123)),
"uint8": spb.NewNumberValue(float64(math.MaxInt8)),
"uint16": spb.NewNumberValue(float64(math.MaxInt16)),
"uint32": spb.NewNumberValue(float64(math.MaxInt32)),
"uint64": spb.NewNumberValue(float64(math.MaxInt64)),
"float32": spb.NewNumberValue(float64(float32(123.456))),
"float64": spb.NewNumberValue(float64(float64(123.456))),
"jsonNumber": spb.NewNumberValue(float64(123.456)),
"string": spb.NewStringValue("hello, world!"),
"bytes": spb.NewStringValue("3q2+7w=="),
"map": spb.NewStructValue(&spb.Struct{Fields: map[string]*spb.Value{
"k1": spb.NewStringValue("v1"),
"k2": spb.NewStringValue("v2"),
Expand Down Expand Up @@ -115,9 +125,13 @@ func TestFromStruct(t *testing.T) {
"nil": spb.NewNullValue(),
"bool": spb.NewBoolValue(false),
"int": spb.NewNumberValue(float64(-123)),
"int8": spb.NewNumberValue(float64(math.MinInt8)),
"int16": spb.NewNumberValue(float64(math.MinInt16)),
"int32": spb.NewNumberValue(float64(math.MinInt32)),
"int64": spb.NewNumberValue(float64(math.MinInt64)),
"uint": spb.NewNumberValue(float64(123)),
"uint8": spb.NewNumberValue(float64(math.MaxInt8)),
"uint16": spb.NewNumberValue(float64(math.MaxInt16)),
"uint32": spb.NewNumberValue(float64(math.MaxInt32)),
"uint64": spb.NewNumberValue(float64(math.MaxInt64)),
"float32": spb.NewNumberValue(float64(float32(123.456))),
Expand All @@ -138,9 +152,13 @@ func TestFromStruct(t *testing.T) {
"nil": nil,
"bool": bool(false),
"int": float64(-123),
"int8": float64(math.MinInt8),
"int16": float64(math.MinInt16),
"int32": float64(math.MinInt32),
"int64": float64(math.MinInt64),
"uint": float64(123),
"uint8": float64(math.MaxInt8),
"uint16": float64(math.MaxInt16),
"uint32": float64(math.MaxInt32),
"uint64": float64(math.MaxInt64),
"float32": float64(float32(123.456)),
Expand Down Expand Up @@ -187,13 +205,18 @@ func TestToListValue(t *testing.T) {
nil,
bool(false),
int(-123),
int8(math.MinInt8),
int16(math.MinInt16),
int32(math.MinInt32),
int64(math.MinInt64),
uint(123),
uint8(math.MaxInt8),
uint16(math.MaxInt16),
uint32(math.MaxInt32),
uint64(math.MaxInt64),
float32(123.456),
float64(123.456),
json.Number("123.456"),
string("hello, world!"),
[]byte("\xde\xad\xbe\xef"),
map[string]any{"k1": "v1", "k2": "v2"},
Expand All @@ -203,13 +226,18 @@ func TestToListValue(t *testing.T) {
spb.NewNullValue(),
spb.NewBoolValue(false),
spb.NewNumberValue(float64(-123)),
spb.NewNumberValue(float64(math.MinInt8)),
spb.NewNumberValue(float64(math.MinInt16)),
spb.NewNumberValue(float64(math.MinInt32)),
spb.NewNumberValue(float64(math.MinInt64)),
spb.NewNumberValue(float64(123)),
spb.NewNumberValue(float64(math.MaxInt8)),
spb.NewNumberValue(float64(math.MaxInt16)),
spb.NewNumberValue(float64(math.MaxInt32)),
spb.NewNumberValue(float64(math.MaxInt64)),
spb.NewNumberValue(float64(float32(123.456))),
spb.NewNumberValue(float64(float64(123.456))),
spb.NewNumberValue(float64(123.456)),
spb.NewStringValue("hello, world!"),
spb.NewStringValue("3q2+7w=="),
spb.NewStructValue(&spb.Struct{Fields: map[string]*spb.Value{
Expand Down Expand Up @@ -259,9 +287,13 @@ func TestFromListValue(t *testing.T) {
spb.NewNullValue(),
spb.NewBoolValue(false),
spb.NewNumberValue(float64(-123)),
spb.NewNumberValue(float64(math.MinInt8)),
spb.NewNumberValue(float64(math.MinInt16)),
spb.NewNumberValue(float64(math.MinInt32)),
spb.NewNumberValue(float64(math.MinInt64)),
spb.NewNumberValue(float64(123)),
spb.NewNumberValue(float64(math.MaxInt8)),
spb.NewNumberValue(float64(math.MaxInt16)),
spb.NewNumberValue(float64(math.MaxInt32)),
spb.NewNumberValue(float64(math.MaxInt64)),
spb.NewNumberValue(float64(float32(123.456))),
Expand All @@ -282,9 +314,13 @@ func TestFromListValue(t *testing.T) {
nil,
bool(false),
float64(-123),
float64(math.MinInt8),
float64(math.MinInt16),
float64(math.MinInt32),
float64(math.MinInt64),
float64(123),
float64(math.MaxInt8),
float64(math.MaxInt16),
float64(math.MaxInt32),
float64(math.MaxInt64),
float64(float32(123.456)),
Expand Down Expand Up @@ -329,6 +365,12 @@ func TestToValue(t *testing.T) {
}, {
in: int(-123),
wantPB: spb.NewNumberValue(float64(-123)),
}, {
in: int8(math.MinInt8),
wantPB: spb.NewNumberValue(float64(math.MinInt8)),
}, {
in: int16(math.MinInt16),
wantPB: spb.NewNumberValue(float64(math.MinInt16)),
}, {
in: int32(math.MinInt32),
wantPB: spb.NewNumberValue(float64(math.MinInt32)),
Expand All @@ -338,6 +380,12 @@ func TestToValue(t *testing.T) {
}, {
in: uint(123),
wantPB: spb.NewNumberValue(float64(123)),
}, {
in: uint8(math.MaxInt8),
wantPB: spb.NewNumberValue(float64(math.MaxInt8)),
}, {
in: uint16(math.MaxInt16),
wantPB: spb.NewNumberValue(float64(math.MaxInt16)),
}, {
in: uint32(math.MaxInt32),
wantPB: spb.NewNumberValue(float64(math.MaxInt32)),
Expand All @@ -350,6 +398,9 @@ func TestToValue(t *testing.T) {
}, {
in: float64(123.456),
wantPB: spb.NewNumberValue(float64(float64(123.456))),
}, {
in: json.Number("123.456"),
wantPB: spb.NewNumberValue(float64(123.456)),
}, {
in: string("hello, world!"),
wantPB: spb.NewStringValue("hello, world!"),
Expand Down Expand Up @@ -428,6 +479,12 @@ func TestFromValue(t *testing.T) {
}, {
in: &spb.Value{Kind: (*spb.Value_NumberValue)(nil)},
want: nil,
}, {
in: spb.NewNumberValue(float64(math.MinInt8)),
want: float64(math.MinInt8),
}, {
in: spb.NewNumberValue(float64(math.MinInt16)),
want: float64(math.MinInt16),
}, {
in: spb.NewNumberValue(float64(math.MinInt32)),
want: float64(math.MinInt32),
Expand All @@ -437,6 +494,12 @@ func TestFromValue(t *testing.T) {
}, {
in: spb.NewNumberValue(float64(123)),
want: float64(123),
}, {
in: spb.NewNumberValue(float64(math.MaxInt8)),
want: float64(math.MaxInt8),
}, {
in: spb.NewNumberValue(float64(math.MaxInt16)),
want: float64(math.MaxInt16),
}, {
in: spb.NewNumberValue(float64(math.MaxInt32)),
want: float64(math.MaxInt32),
Expand Down

0 comments on commit b36f8e0

Please sign in to comment.