blob: e491fd1e8cc5d4119423f2a9311512419808966e [file] [log] [blame]
Joe Tsai88bc5a72018-11-05 11:42:22 -08001// Copyright 2018 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Package value provides functionality for wrapping Go values to implement
6// protoreflect values.
7package value
8
9import (
10 "fmt"
11 "reflect"
12
13 pref "github.com/golang/protobuf/v2/reflect/protoreflect"
14)
15
16// Unwrapper unwraps the value to the underlying value.
Joe Tsai4b7aff62018-11-14 14:05:19 -080017// This is implemented by List and Map.
Joe Tsai88bc5a72018-11-05 11:42:22 -080018type Unwrapper interface {
19 Unwrap() interface{}
20}
21
22// messageV1 is the protoV1.Message interface.
23type messageV1 = interface {
24 Reset()
25 String() string
26 ProtoMessage()
27}
28
29var (
30 boolType = reflect.TypeOf(bool(false))
31 int32Type = reflect.TypeOf(int32(0))
32 int64Type = reflect.TypeOf(int64(0))
33 uint32Type = reflect.TypeOf(uint32(0))
34 uint64Type = reflect.TypeOf(uint64(0))
35 float32Type = reflect.TypeOf(float32(0))
36 float64Type = reflect.TypeOf(float64(0))
37 stringType = reflect.TypeOf(string(""))
38 bytesType = reflect.TypeOf([]byte(nil))
39
40 enumIfaceV2 = reflect.TypeOf((*pref.ProtoEnum)(nil)).Elem()
41 messageIfaceV1 = reflect.TypeOf((*messageV1)(nil)).Elem()
42 messageIfaceV2 = reflect.TypeOf((*pref.ProtoMessage)(nil)).Elem()
43
44 byteType = reflect.TypeOf(byte(0))
45)
46
47// NewConverter matches a Go type with a protobuf kind and returns a Converter
48// that converts between the two. NewConverter panics if it unable to provide a
49// conversion between the two. The Converter methods also panic when they are
50// called on incorrect Go types.
51//
52// This matcher deliberately supports a wider range of Go types than what
53// protoc-gen-go historically generated to be able to automatically wrap some
54// v1 messages generated by other forks of protoc-gen-go.
55func NewConverter(t reflect.Type, k pref.Kind) Converter {
Joe Tsaif0c01e42018-11-06 13:05:20 -080056 return NewLegacyConverter(t, k, nil, nil, nil)
Joe Tsai88bc5a72018-11-05 11:42:22 -080057}
58
Joe Tsaif0c01e42018-11-06 13:05:20 -080059// Legacy enums and messages do not self-report their own protoreflect types.
60// Thus, the caller needs to provide functions for retrieving those when
61// a v1 enum or message is encountered.
62type (
63 enumTypeOf = func(reflect.Type) pref.EnumType
64 messageTypeOf = func(reflect.Type) pref.MessageType
65 messageValueOf = func(reflect.Value) pref.ProtoMessage
66)
67
Joe Tsai88bc5a72018-11-05 11:42:22 -080068// NewLegacyConverter is identical to NewConverter,
69// but supports wrapping legacy v1 messages to implement the v2 message API
Joe Tsaif0c01e42018-11-06 13:05:20 -080070// using the provided enumTypeOf, messageTypeOf and messageValueOf functions.
Joe Tsai88bc5a72018-11-05 11:42:22 -080071// The wrapped message must implement Unwrapper.
Joe Tsaif0c01e42018-11-06 13:05:20 -080072func NewLegacyConverter(t reflect.Type, k pref.Kind, etOf enumTypeOf, mtOf messageTypeOf, mvOf messageValueOf) Converter {
Joe Tsai88bc5a72018-11-05 11:42:22 -080073 switch k {
74 case pref.BoolKind:
75 if t.Kind() == reflect.Bool {
76 return makeScalarConverter(t, boolType)
77 }
78 case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind:
79 if t.Kind() == reflect.Int32 {
80 return makeScalarConverter(t, int32Type)
81 }
82 case pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind:
83 if t.Kind() == reflect.Int64 {
84 return makeScalarConverter(t, int64Type)
85 }
86 case pref.Uint32Kind, pref.Fixed32Kind:
87 if t.Kind() == reflect.Uint32 {
88 return makeScalarConverter(t, uint32Type)
89 }
90 case pref.Uint64Kind, pref.Fixed64Kind:
91 if t.Kind() == reflect.Uint64 {
92 return makeScalarConverter(t, uint64Type)
93 }
94 case pref.FloatKind:
95 if t.Kind() == reflect.Float32 {
96 return makeScalarConverter(t, float32Type)
97 }
98 case pref.DoubleKind:
99 if t.Kind() == reflect.Float64 {
100 return makeScalarConverter(t, float64Type)
101 }
102 case pref.StringKind:
103 if t.Kind() == reflect.String || (t.Kind() == reflect.Slice && t.Elem() == byteType) {
104 return makeScalarConverter(t, stringType)
105 }
106 case pref.BytesKind:
107 if t.Kind() == reflect.String || (t.Kind() == reflect.Slice && t.Elem() == byteType) {
108 return makeScalarConverter(t, bytesType)
109 }
110 case pref.EnumKind:
111 // Handle v2 enums, which must satisfy the proto.Enum interface.
112 if t.Kind() != reflect.Ptr && t.Implements(enumIfaceV2) {
113 et := reflect.Zero(t).Interface().(pref.ProtoEnum).ProtoReflect().Type()
114 return Converter{
Joe Tsai6f9095c2018-11-10 14:12:21 -0800115 PBValueOf: func(v reflect.Value) pref.Value {
Joe Tsai88bc5a72018-11-05 11:42:22 -0800116 if v.Type() != t {
117 panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), t))
118 }
119 e := v.Interface().(pref.ProtoEnum)
120 return pref.ValueOf(e.ProtoReflect().Number())
121 },
Joe Tsai6f9095c2018-11-10 14:12:21 -0800122 GoValueOf: func(v pref.Value) reflect.Value {
Joe Tsai1c40f492018-11-09 17:02:57 -0800123 rv := reflect.ValueOf(et.New(v.Enum()))
Joe Tsai88bc5a72018-11-05 11:42:22 -0800124 if rv.Type() != t {
125 panic(fmt.Sprintf("invalid type: got %v, want %v", rv.Type(), t))
126 }
127 return rv
128 },
Joe Tsai6f9095c2018-11-10 14:12:21 -0800129 EnumType: et,
Joe Tsai88bc5a72018-11-05 11:42:22 -0800130 }
131 }
132
133 // Handle v1 enums, which we identify as simply a named int32 type.
Joe Tsaif0c01e42018-11-06 13:05:20 -0800134 if etOf != nil && t.PkgPath() != "" && t.Kind() == reflect.Int32 {
135 et := etOf(t)
Joe Tsai88bc5a72018-11-05 11:42:22 -0800136 return Converter{
Joe Tsai6f9095c2018-11-10 14:12:21 -0800137 PBValueOf: func(v reflect.Value) pref.Value {
Joe Tsai88bc5a72018-11-05 11:42:22 -0800138 if v.Type() != t {
139 panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), t))
140 }
141 return pref.ValueOf(pref.EnumNumber(v.Int()))
142 },
Joe Tsai6f9095c2018-11-10 14:12:21 -0800143 GoValueOf: func(v pref.Value) reflect.Value {
Joe Tsai88bc5a72018-11-05 11:42:22 -0800144 return reflect.ValueOf(v.Enum()).Convert(t)
145 },
Joe Tsai6f9095c2018-11-10 14:12:21 -0800146 EnumType: et,
147 IsLegacy: true,
Joe Tsai88bc5a72018-11-05 11:42:22 -0800148 }
149 }
150 case pref.MessageKind, pref.GroupKind:
151 // Handle v2 messages, which must satisfy the proto.Message interface.
152 if t.Kind() == reflect.Ptr && t.Implements(messageIfaceV2) {
153 mt := reflect.Zero(t).Interface().(pref.ProtoMessage).ProtoReflect().Type()
154 return Converter{
Joe Tsai6f9095c2018-11-10 14:12:21 -0800155 PBValueOf: func(v reflect.Value) pref.Value {
Joe Tsai88bc5a72018-11-05 11:42:22 -0800156 if v.Type() != t {
157 panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), t))
158 }
159 return pref.ValueOf(v.Interface())
160 },
Joe Tsai6f9095c2018-11-10 14:12:21 -0800161 GoValueOf: func(v pref.Value) reflect.Value {
Joe Tsai88bc5a72018-11-05 11:42:22 -0800162 rv := reflect.ValueOf(v.Message())
163 if rv.Type() != t {
164 panic(fmt.Sprintf("invalid type: got %v, want %v", rv.Type(), t))
165 }
166 return rv
167 },
Joe Tsai6f9095c2018-11-10 14:12:21 -0800168 MessageType: mt,
Joe Tsai88bc5a72018-11-05 11:42:22 -0800169 }
170 }
171
172 // Handle v1 messages, which we need to wrap as a v2 message.
Joe Tsaif0c01e42018-11-06 13:05:20 -0800173 if mtOf != nil && t.Kind() == reflect.Ptr && t.Implements(messageIfaceV1) {
174 mt := mtOf(t)
Joe Tsai88bc5a72018-11-05 11:42:22 -0800175 return Converter{
Joe Tsai6f9095c2018-11-10 14:12:21 -0800176 PBValueOf: func(v reflect.Value) pref.Value {
Joe Tsai88bc5a72018-11-05 11:42:22 -0800177 if v.Type() != t {
178 panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), t))
179 }
Joe Tsaif0c01e42018-11-06 13:05:20 -0800180 return pref.ValueOf(mvOf(v).ProtoReflect())
Joe Tsai88bc5a72018-11-05 11:42:22 -0800181 },
Joe Tsai6f9095c2018-11-10 14:12:21 -0800182 GoValueOf: func(v pref.Value) reflect.Value {
Joe Tsai88bc5a72018-11-05 11:42:22 -0800183 rv := reflect.ValueOf(v.Message().(Unwrapper).Unwrap())
184 if rv.Type() != t {
185 panic(fmt.Sprintf("invalid type: got %v, want %v", rv.Type(), t))
186 }
187 return rv
188 },
Joe Tsai6f9095c2018-11-10 14:12:21 -0800189 MessageType: mt,
190 IsLegacy: true,
Joe Tsai88bc5a72018-11-05 11:42:22 -0800191 }
192 }
193 }
194 panic(fmt.Sprintf("invalid Go type %v for protobuf kind %v", t, k))
195}
196
197func makeScalarConverter(goType, pbType reflect.Type) Converter {
198 return Converter{
Joe Tsai6f9095c2018-11-10 14:12:21 -0800199 PBValueOf: func(v reflect.Value) pref.Value {
Joe Tsai88bc5a72018-11-05 11:42:22 -0800200 if v.Type() != goType {
201 panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), goType))
202 }
203 if goType.Kind() == reflect.String && pbType.Kind() == reflect.Slice && v.Len() == 0 {
204 return pref.ValueOf([]byte(nil)) // ensure empty string is []byte(nil)
205 }
206 return pref.ValueOf(v.Convert(pbType).Interface())
207 },
Joe Tsai6f9095c2018-11-10 14:12:21 -0800208 GoValueOf: func(v pref.Value) reflect.Value {
Joe Tsai88bc5a72018-11-05 11:42:22 -0800209 rv := reflect.ValueOf(v.Interface())
210 if rv.Type() != pbType {
211 panic(fmt.Sprintf("invalid type: got %v, want %v", rv.Type(), pbType))
212 }
213 if pbType.Kind() == reflect.String && goType.Kind() == reflect.Slice && rv.Len() == 0 {
214 return reflect.Zero(goType) // ensure empty string is []byte(nil)
215 }
216 return rv.Convert(goType)
217 },
218 }
219}
220
221// Converter provides functions for converting to/from Go reflect.Value types
222// and protobuf protoreflect.Value types.
223type Converter struct {
Joe Tsai6f9095c2018-11-10 14:12:21 -0800224 PBValueOf func(reflect.Value) pref.Value
225 GoValueOf func(pref.Value) reflect.Value
226 EnumType pref.EnumType
227 MessageType pref.MessageType
228 IsLegacy bool
Joe Tsai88bc5a72018-11-05 11:42:22 -0800229}