|
|
@@ -9,6 +9,7 @@ import (
|
|
|
"reflect"
|
|
|
"sync"
|
|
|
|
|
|
+ "github.com/golang/protobuf/v2/internal/value"
|
|
|
"github.com/golang/protobuf/v2/reflect/protoreflect"
|
|
|
)
|
|
|
|
|
|
@@ -90,4 +91,205 @@ func (p goMessage) GoType() reflect.Type {
|
|
|
return p.goType
|
|
|
}
|
|
|
|
|
|
-// TODO: add extension support.
|
|
|
+// GoExtension is a constructor for a protoreflect.ExtensionType.
|
|
|
+type GoExtension struct {
|
|
|
+ protoreflect.ExtensionDescriptor
|
|
|
+
|
|
|
+ // NewEnum returns a concrete proto.Enum value with the given enum number.
|
|
|
+ // The constructor must be provided if protoreflect.ExtensionDescriptor.Kind
|
|
|
+ // is protoreflect.EnumKind.
|
|
|
+ //
|
|
|
+ // The returned enum must represent an protoreflect.EnumDescriptor
|
|
|
+ // that matches protoreflect.ExtensionDescriptor.EnumType.
|
|
|
+ NewEnum func(protoreflect.EnumNumber) protoreflect.ProtoEnum
|
|
|
+
|
|
|
+ // NewMessage returns a new empty proto.Message instance.
|
|
|
+ // The constructor must be provided if protoreflect.ExtensionDescriptor.Kind
|
|
|
+ // is protoreflect.MessageKind or protoreflect.GroupKind.
|
|
|
+ //
|
|
|
+ // The returned message must represent an protoreflect.MessageDescriptor
|
|
|
+ // that matches protoreflect.ExtensionDescriptor.MessageType.
|
|
|
+ NewMessage func() protoreflect.ProtoMessage
|
|
|
+
|
|
|
+ // TODO: Separate NewEnum and NewMessage constructors make it possible for
|
|
|
+ // users to provide a constructor that returns a Go type does not match
|
|
|
+ // the corresponding protobuf descriptor in ExtensionDescriptor.
|
|
|
+ // Checking for correctness is hard since descriptors are not comparable.
|
|
|
+ //
|
|
|
+ // An alternative API is for ExtensionDescriptor.{EnumType,MessageType}
|
|
|
+ // to document that it must implement protoreflect.{EnumType,MessageType}.
|
|
|
+
|
|
|
+ // TODO: Support custom Go types? If so, the user needs to provide their
|
|
|
+ // own New, ValueOf, and InterfaceOf adapters.
|
|
|
+
|
|
|
+ once sync.Once
|
|
|
+ new func() interface{}
|
|
|
+ goType reflect.Type
|
|
|
+ valueOf func(v interface{}) protoreflect.Value
|
|
|
+ interfaceOf func(v protoreflect.Value) interface{}
|
|
|
+}
|
|
|
+type goExtension struct{ *GoExtension }
|
|
|
+
|
|
|
+// NewGoExtension creates a new protoreflect.ExtensionType.
|
|
|
+//
|
|
|
+// The Go type is currently determined automatically (although custom Go types
|
|
|
+// may be supported in the future). The type is T for scalars and
|
|
|
+// *[]T for vectors. Maps are not valid in extension fields.
|
|
|
+// The type T is determined as follows:
|
|
|
+//
|
|
|
+// +------------+-------------------------------------+
|
|
|
+// | Go type | Protobuf kind |
|
|
|
+// +------------+-------------------------------------+
|
|
|
+// | bool | BoolKind |
|
|
|
+// | int32 | Int32Kind, Sint32Kind, Sfixed32Kind |
|
|
|
+// | int64 | Int64Kind, Sint64Kind, Sfixed64Kind |
|
|
|
+// | uint32 | Uint32Kind, Fixed32Kind |
|
|
|
+// | uint64 | Uint64Kind, Fixed64Kind |
|
|
|
+// | float32 | FloatKind |
|
|
|
+// | float64 | DoubleKind |
|
|
|
+// | string | StringKind |
|
|
|
+// | []byte | BytesKind |
|
|
|
+// | E | EnumKind |
|
|
|
+// | M | MessageKind, GroupKind |
|
|
|
+// +------------+-------------------------------------+
|
|
|
+//
|
|
|
+// The type E is the concrete enum type returned by NewEnum,
|
|
|
+// which is often, but not required to be, a named int32 type.
|
|
|
+// The type M is the concrete message type returned by NewMessage,
|
|
|
+// which is often, but not required to be, a pointer to a named struct type.
|
|
|
+//
|
|
|
+// The caller must relinquish full ownership of the input t and must not
|
|
|
+// access or mutate any fields.
|
|
|
+func NewGoExtension(t *GoExtension) protoreflect.ExtensionType {
|
|
|
+ if t.ExtendedType() == nil {
|
|
|
+ panic("field descriptor does not extend a message")
|
|
|
+ }
|
|
|
+ switch t.Kind() {
|
|
|
+ case protoreflect.EnumKind:
|
|
|
+ if t.NewEnum == nil {
|
|
|
+ panic("enum constructor not provided for enum kind")
|
|
|
+ }
|
|
|
+ if t.NewMessage != nil {
|
|
|
+ panic("message constructor provided for enum kind")
|
|
|
+ }
|
|
|
+ case protoreflect.MessageKind, protoreflect.GroupKind:
|
|
|
+ if t.NewMessage == nil {
|
|
|
+ panic("message constructor not provided for message kind")
|
|
|
+ }
|
|
|
+ if t.NewEnum != nil {
|
|
|
+ panic("enum constructor provided for message kind")
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ if t.NewMessage != nil || t.NewEnum != nil {
|
|
|
+ panic(fmt.Sprintf("enum or message constructor provided for %v kind", t.Kind()))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return goExtension{t}
|
|
|
+}
|
|
|
+func (p goExtension) GoNew() interface{} {
|
|
|
+ p.lazyInit()
|
|
|
+ v := p.new()
|
|
|
+ if reflect.TypeOf(v) != p.goType {
|
|
|
+ panic(fmt.Sprintf("invalid type: got %T, want %v", v, p.goType))
|
|
|
+ }
|
|
|
+ return v
|
|
|
+}
|
|
|
+func (p goExtension) GoType() reflect.Type {
|
|
|
+ p.lazyInit()
|
|
|
+ return p.goType
|
|
|
+}
|
|
|
+func (p goExtension) ValueOf(v interface{}) protoreflect.Value {
|
|
|
+ p.lazyInit()
|
|
|
+ if reflect.TypeOf(v) != p.goType {
|
|
|
+ panic(fmt.Sprintf("invalid type: got %T, want %v", v, p.goType))
|
|
|
+ }
|
|
|
+ return p.valueOf(v)
|
|
|
+}
|
|
|
+func (p goExtension) InterfaceOf(pv protoreflect.Value) interface{} {
|
|
|
+ p.lazyInit()
|
|
|
+ v := p.interfaceOf(pv)
|
|
|
+ if reflect.TypeOf(v) != p.goType {
|
|
|
+ panic(fmt.Sprintf("invalid type: got %T, want %v", v, p.goType))
|
|
|
+ }
|
|
|
+ return v
|
|
|
+}
|
|
|
+func (p goExtension) lazyInit() {
|
|
|
+ p.once.Do(func() {
|
|
|
+ switch p.Cardinality() {
|
|
|
+ case protoreflect.Optional:
|
|
|
+ switch p.Kind() {
|
|
|
+ case protoreflect.EnumKind:
|
|
|
+ p.goType = reflect.TypeOf(p.NewEnum(0))
|
|
|
+ p.new = func() interface{} { return p.NewEnum(p.Default().Enum()) }
|
|
|
+ p.valueOf = func(v interface{}) protoreflect.Value {
|
|
|
+ ev := v.(protoreflect.ProtoEnum).ProtoReflect()
|
|
|
+ return protoreflect.ValueOf(ev.Number())
|
|
|
+ }
|
|
|
+ p.interfaceOf = func(pv protoreflect.Value) interface{} {
|
|
|
+ return p.NewEnum(pv.Enum())
|
|
|
+ }
|
|
|
+ case protoreflect.MessageKind, protoreflect.GroupKind:
|
|
|
+ p.goType = reflect.TypeOf(p.NewMessage())
|
|
|
+ p.new = func() interface{} { return p.NewMessage() }
|
|
|
+ p.valueOf = func(v interface{}) protoreflect.Value {
|
|
|
+ return protoreflect.ValueOf(v)
|
|
|
+ }
|
|
|
+ p.interfaceOf = func(pv protoreflect.Value) interface{} {
|
|
|
+ return pv.Message().Interface()
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ p.goType = goTypeForPBKind[p.Kind()]
|
|
|
+ p.new = func() interface{} { return p.Default().Interface() }
|
|
|
+ p.valueOf = func(v interface{}) protoreflect.Value {
|
|
|
+ return protoreflect.ValueOf(v)
|
|
|
+ }
|
|
|
+ p.interfaceOf = func(pv protoreflect.Value) interface{} {
|
|
|
+ v := pv.Interface()
|
|
|
+ return v
|
|
|
+ }
|
|
|
+ }
|
|
|
+ case protoreflect.Repeated:
|
|
|
+ var goType reflect.Type
|
|
|
+ switch p.Kind() {
|
|
|
+ case protoreflect.EnumKind:
|
|
|
+ goType = reflect.TypeOf(p.NewEnum(p.Default().Enum()))
|
|
|
+ case protoreflect.MessageKind, protoreflect.GroupKind:
|
|
|
+ goType = reflect.TypeOf(p.NewMessage())
|
|
|
+ default:
|
|
|
+ goType = goTypeForPBKind[p.Kind()]
|
|
|
+ }
|
|
|
+ c := value.NewConverter(goType, p.Kind())
|
|
|
+ p.goType = reflect.PtrTo(reflect.SliceOf(goType))
|
|
|
+ p.new = func() interface{} { return reflect.New(p.goType.Elem()).Interface() }
|
|
|
+ p.valueOf = func(v interface{}) protoreflect.Value {
|
|
|
+ return protoreflect.ValueOf(value.VectorOf(v, c))
|
|
|
+ }
|
|
|
+ p.interfaceOf = func(v protoreflect.Value) interface{} {
|
|
|
+ // TODO: Can we assume that Vector implementations know how
|
|
|
+ // to unwrap themselves?
|
|
|
+ // Should this be part of the public API in protoreflect?
|
|
|
+ return v.Vector().(value.Unwrapper).Unwrap()
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ panic(fmt.Sprintf("invalid cardinality: %v", p.Cardinality()))
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+var goTypeForPBKind = map[protoreflect.Kind]reflect.Type{
|
|
|
+ protoreflect.BoolKind: reflect.TypeOf(bool(false)),
|
|
|
+ protoreflect.Int32Kind: reflect.TypeOf(int32(0)),
|
|
|
+ protoreflect.Sint32Kind: reflect.TypeOf(int32(0)),
|
|
|
+ protoreflect.Fixed32Kind: reflect.TypeOf(int32(0)),
|
|
|
+ protoreflect.Int64Kind: reflect.TypeOf(int64(0)),
|
|
|
+ protoreflect.Sint64Kind: reflect.TypeOf(int64(0)),
|
|
|
+ protoreflect.Fixed64Kind: reflect.TypeOf(int64(0)),
|
|
|
+ protoreflect.Uint32Kind: reflect.TypeOf(uint32(0)),
|
|
|
+ protoreflect.Sfixed32Kind: reflect.TypeOf(uint32(0)),
|
|
|
+ protoreflect.Uint64Kind: reflect.TypeOf(uint64(0)),
|
|
|
+ protoreflect.Sfixed64Kind: reflect.TypeOf(uint64(0)),
|
|
|
+ protoreflect.FloatKind: reflect.TypeOf(float32(0)),
|
|
|
+ protoreflect.DoubleKind: reflect.TypeOf(float64(0)),
|
|
|
+ protoreflect.StringKind: reflect.TypeOf(string("")),
|
|
|
+ protoreflect.BytesKind: reflect.TypeOf([]byte(nil)),
|
|
|
+}
|