mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2025-02-21 00:39:54 +00:00
This CL introduces generation of specialized APIs directly into the generated packages for certain well-known types. This follows the pattern set forth by the other language implementations that have specialized generated support for certain well-known types. Overview of new API: package anypb func MarshalFrom(*Any, proto.Message, proto.MarshalOptions) error func UnmarshalTo(*Any, proto.Message, proto.UnmarshalOptions) error func UnmarshalNew(*Any, proto.UnmarshalOptions) (proto.Message, error) func (*Any) MessageIs(proto.Message) bool func (*Any) MessageName() protoreflect.FullName func (*Any) MarshalFrom(proto.Message) error func (*Any) UnmarshalTo(proto.Message) error func (*Any) UnmarshalNew() (proto.Message, error) package timestamppb func Now() *Timestamp func New(time.Time) *Timestamp func (*Timestamp) AsTime() time.Time func (*Timestamp) IsValid() bool func (*Timestamp) CheckValid() error package durationpb func New(time.Duration) *Duration func (*Duration) AsDuration() time.Duration func (*Duration) IsValid() bool func (*Duration) CheckValid() error package structpb func NewStruct(map[string]interface{}) (*Struct, error) func (*Struct) AsMap() map[string]interface{} func (*Struct) MarshalJSON() ([]byte, error) func (*Struct) UnmarshalJSON(b []byte) error func NewList([]interface{}) (*ListValue, error) func (*ListValue) AsSlice() []interface{} func (*ListValue) MarshalJSON() ([]byte, error) func (*ListValue) UnmarshalJSON(b []byte) error func NewValue(interface{}) (*Value, error) func NewNullValue() *Value func NewBoolValue(bool) *Value func NewNumberValue(float64) *Value func NewStringValue(string) *Value func NewStructValue(*Struct) *Value func NewListValue(*ListValue) *Value func (*Value) AsInterface() interface{} func (*Value) MarshalJSON() ([]byte, error) func (*Value) UnmarshalJSON(b []byte) error package fieldmaskpb func New(proto.Message, ...string) (*FieldMask, error) func Union(*FieldMask, *FieldMask, ...*FieldMask) *FieldMask func Intersect(*FieldMask, *FieldMask, ...*FieldMask) *FieldMask func (*FieldMask) IsValid(proto.Message) bool func (*FieldMask) Append(proto.Message, ...string) error func (*FieldMask) Normalize() package wrapperspb func Bool(bool) *BoolValue func Int32(int32) *Int32Value func Int64(int64) *Int64Value func UInt32(uint32) *UInt32Value func UInt64(uint64) *UInt64Value func Float(float32) *FloatValue func Double(float64) *DoubleValue func String(string) *StringValue func Bytes([]byte) *BytesValue This functionality expands upon and supersedes the older github.com/golang/protobuf/ptypes package, which provided helpers for Any, Timestamp, and Duration. Comparison with older ptypes package: * ptypes.AnyMessageName is replaced by anypb.Any.MessageName. The former returned an error for malformed type URLs, while the latter simply returns an empty string. * ptypes.Is is replaced by anypb.Any.MessageIs. * ptypes.Empty has no direct replacement as it is equivalent to: mt, err := protoregistry.GlobalTypes.FindMessageByURL(any.GetTypeUrl()) if err != nil { return nil, err } return mt.New().Interface(), nil Analysis of user code revealed that this function is seldom used. * ptypes.MarshalAny is replaced by anypb.Any.MarshalFrom. The former creates a new Any message and returns it, while the latter is a method that modifies the receiver. * ptypes.UnmarshalAny is replaced by anypb.Any.UnmarshalTo. * ptypes.DynamicAny is loosely replaced by anypb.Any.UnmarshalNew. The DynamicAny type is a custom proto.Message that is special to ptypes.UnmarshalAny where it would allocate a new message and store it into the DynamicAny instance. The UnmarshalNew method accomplishes the equivalent functionality in a more direct fashion. * ptypes.TimestampNow is replaced by timestamppb.Now. * ptypes.TimestampProto is replaced by timestamppb.New. The former returned an error if the timestamp was outside the 10000-year range recommended by timestamp.proto, while the latter always succeeded. To preserve the behavior of the former validation check, the replacement can additionally call the timestamppb.Timestamp.CheckValid method. * ptypes.Timestamp is replaced by timestamppb.Timestamp.AsTime. The former returned an error if the timestamp was outside the 10000-year range recommended by timestamp.proto, while the latter always succeeded. To preserve the behavior of the former validation check, the replacement can additionally call the timestamppb.Timestamp.CheckValid method. * ptypes.TimestampString has no direct replacement as it is equivalent to: ts.AsTime().Format(time.RFC3339Nano) * ptypes.DurationProto is replaced by durationpb.New. * ptypes.Duration is replaced by durationpb.Duration.AsDuration. The former returned an error if the duration would overflow when converting to a time.Duration, while the latter uses saturation arithmetic (similiar to the time package itself). Underflow resulted in time.Duration(math.MinInt64), while overflow resulted in time.Duration(math.MaxInt64). To preserve the behavior of former validation checks, the replacement can call the durationpb.Duration.CheckValid method and check whether the duration is fixed to one of the overflow values. Change-Id: Ia996b1037a1fcafced7c7e10e9408ef7fa22863a Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/225298 Reviewed-by: Damien Neil <dneil@google.com>
178 lines
4.8 KiB
Go
178 lines
4.8 KiB
Go
// Copyright 2018 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package impl
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"strconv"
|
|
|
|
"google.golang.org/protobuf/encoding/prototext"
|
|
"google.golang.org/protobuf/internal/errors"
|
|
"google.golang.org/protobuf/proto"
|
|
pref "google.golang.org/protobuf/reflect/protoreflect"
|
|
piface "google.golang.org/protobuf/runtime/protoiface"
|
|
)
|
|
|
|
// Export is a zero-length named type that exists only to export a set of
|
|
// functions that we do not want to appear in godoc.
|
|
type Export struct{}
|
|
|
|
// NewError formats a string according to the format specifier and arguments and
|
|
// returns an error that has a "proto" prefix.
|
|
func (Export) NewError(f string, x ...interface{}) error {
|
|
return errors.New(f, x...)
|
|
}
|
|
|
|
// enum is any enum type generated by protoc-gen-go
|
|
// and must be a named int32 type.
|
|
type enum = interface{}
|
|
|
|
// EnumOf returns the protoreflect.Enum interface over e.
|
|
// It returns nil if e is nil.
|
|
func (Export) EnumOf(e enum) pref.Enum {
|
|
switch e := e.(type) {
|
|
case nil:
|
|
return nil
|
|
case pref.Enum:
|
|
return e
|
|
default:
|
|
return legacyWrapEnum(reflect.ValueOf(e))
|
|
}
|
|
}
|
|
|
|
// EnumDescriptorOf returns the protoreflect.EnumDescriptor for e.
|
|
// It returns nil if e is nil.
|
|
func (Export) EnumDescriptorOf(e enum) pref.EnumDescriptor {
|
|
switch e := e.(type) {
|
|
case nil:
|
|
return nil
|
|
case pref.Enum:
|
|
return e.Descriptor()
|
|
default:
|
|
return LegacyLoadEnumDesc(reflect.TypeOf(e))
|
|
}
|
|
}
|
|
|
|
// EnumTypeOf returns the protoreflect.EnumType for e.
|
|
// It returns nil if e is nil.
|
|
func (Export) EnumTypeOf(e enum) pref.EnumType {
|
|
switch e := e.(type) {
|
|
case nil:
|
|
return nil
|
|
case pref.Enum:
|
|
return e.Type()
|
|
default:
|
|
return legacyLoadEnumType(reflect.TypeOf(e))
|
|
}
|
|
}
|
|
|
|
// EnumStringOf returns the enum value as a string, either as the name if
|
|
// the number is resolvable, or the number formatted as a string.
|
|
func (Export) EnumStringOf(ed pref.EnumDescriptor, n pref.EnumNumber) string {
|
|
ev := ed.Values().ByNumber(n)
|
|
if ev != nil {
|
|
return string(ev.Name())
|
|
}
|
|
return strconv.Itoa(int(n))
|
|
}
|
|
|
|
// message is any message type generated by protoc-gen-go
|
|
// and must be a pointer to a named struct type.
|
|
type message = interface{}
|
|
|
|
// legacyMessageWrapper wraps a v2 message as a v1 message.
|
|
type legacyMessageWrapper struct{ m pref.ProtoMessage }
|
|
|
|
func (m legacyMessageWrapper) Reset() { proto.Reset(m.m) }
|
|
func (m legacyMessageWrapper) String() string { return Export{}.MessageStringOf(m.m) }
|
|
func (m legacyMessageWrapper) ProtoMessage() {}
|
|
|
|
// ProtoMessageV1Of converts either a v1 or v2 message to a v1 message.
|
|
// It returns nil if m is nil.
|
|
func (Export) ProtoMessageV1Of(m message) piface.MessageV1 {
|
|
switch mv := m.(type) {
|
|
case nil:
|
|
return nil
|
|
case piface.MessageV1:
|
|
return mv
|
|
case unwrapper:
|
|
return Export{}.ProtoMessageV1Of(mv.protoUnwrap())
|
|
case pref.ProtoMessage:
|
|
return legacyMessageWrapper{mv}
|
|
default:
|
|
panic(fmt.Sprintf("message %T is neither a v1 or v2 Message", m))
|
|
}
|
|
}
|
|
|
|
func (Export) protoMessageV2Of(m message) pref.ProtoMessage {
|
|
switch mv := m.(type) {
|
|
case nil:
|
|
return nil
|
|
case pref.ProtoMessage:
|
|
return mv
|
|
case legacyMessageWrapper:
|
|
return mv.m
|
|
case piface.MessageV1:
|
|
return nil
|
|
default:
|
|
panic(fmt.Sprintf("message %T is neither a v1 or v2 Message", m))
|
|
}
|
|
}
|
|
|
|
// ProtoMessageV2Of converts either a v1 or v2 message to a v2 message.
|
|
// It returns nil if m is nil.
|
|
func (Export) ProtoMessageV2Of(m message) pref.ProtoMessage {
|
|
if m == nil {
|
|
return nil
|
|
}
|
|
if mv := (Export{}).protoMessageV2Of(m); mv != nil {
|
|
return mv
|
|
}
|
|
return legacyWrapMessage(reflect.ValueOf(m)).Interface()
|
|
}
|
|
|
|
// MessageOf returns the protoreflect.Message interface over m.
|
|
// It returns nil if m is nil.
|
|
func (Export) MessageOf(m message) pref.Message {
|
|
if m == nil {
|
|
return nil
|
|
}
|
|
if mv := (Export{}).protoMessageV2Of(m); mv != nil {
|
|
return mv.ProtoReflect()
|
|
}
|
|
return legacyWrapMessage(reflect.ValueOf(m))
|
|
}
|
|
|
|
// MessageDescriptorOf returns the protoreflect.MessageDescriptor for m.
|
|
// It returns nil if m is nil.
|
|
func (Export) MessageDescriptorOf(m message) pref.MessageDescriptor {
|
|
if m == nil {
|
|
return nil
|
|
}
|
|
if mv := (Export{}).protoMessageV2Of(m); mv != nil {
|
|
return mv.ProtoReflect().Descriptor()
|
|
}
|
|
return LegacyLoadMessageDesc(reflect.TypeOf(m))
|
|
}
|
|
|
|
// MessageTypeOf returns the protoreflect.MessageType for m.
|
|
// It returns nil if m is nil.
|
|
func (Export) MessageTypeOf(m message) pref.MessageType {
|
|
if m == nil {
|
|
return nil
|
|
}
|
|
if mv := (Export{}).protoMessageV2Of(m); mv != nil {
|
|
return mv.ProtoReflect().Type()
|
|
}
|
|
return legacyLoadMessageInfo(reflect.TypeOf(m), "")
|
|
}
|
|
|
|
// MessageStringOf returns the message value as a string,
|
|
// which is the message serialized in the protobuf text format.
|
|
func (Export) MessageStringOf(m pref.ProtoMessage) string {
|
|
return prototext.MarshalOptions{Multiline: false}.Format(m)
|
|
}
|