diff --git a/vtcompare.go b/protobuf-go-lite.go similarity index 81% rename from vtcompare.go rename to protobuf-go-lite.go index 9aede170..0ecd62ac 100644 --- a/vtcompare.go +++ b/protobuf-go-lite.go @@ -1,5 +1,12 @@ package protobuf_go_lite +// Message is the base vtprotobuf message marshal/unmarshal interface. +type Message interface { + MarshalVT() ([]byte, error) + UnmarshalVT([]byte) error + Reset() +} + // EqualVT is a message with a EqualVT function (VTProtobuf). type EqualVT[T comparable] interface { comparable @@ -10,7 +17,7 @@ type EqualVT[T comparable] interface { // CompareEqualVT returns a compare function to compare two VTProtobuf messages. func CompareEqualVT[T EqualVT[T]]() func(t1, t2 T) bool { return func(t1, t2 T) bool { - return IsEqualVT[T](t1, t2) + return IsEqualVT(t1, t2) } } diff --git a/vtcompare_test.go b/protobuf-go-lite_test.go similarity index 100% rename from vtcompare_test.go rename to protobuf-go-lite_test.go diff --git a/testproto/wkt/wkt.pb.go b/testproto/wkt/wkt.pb.go index 171d96ce..d99ec062 100644 --- a/testproto/wkt/wkt.pb.go +++ b/testproto/wkt/wkt.pb.go @@ -9,6 +9,7 @@ import ( io "io" protohelpers "github.com/aperturerobotics/protobuf-go-lite/protohelpers" + anypb "github.com/aperturerobotics/protobuf-go-lite/types/known/anypb" durationpb "github.com/aperturerobotics/protobuf-go-lite/types/known/durationpb" emptypb "github.com/aperturerobotics/protobuf-go-lite/types/known/emptypb" structpb "github.com/aperturerobotics/protobuf-go-lite/types/known/structpb" @@ -18,6 +19,7 @@ import ( type MessageWithWKT struct { unknownFields []byte + Any *anypb.Any `protobuf:"bytes,1,opt,name=any,proto3" json:"any,omitempty"` Duration *durationpb.Duration `protobuf:"bytes,2,opt,name=duration,proto3" json:"duration,omitempty"` Empty *emptypb.Empty `protobuf:"bytes,3,opt,name=empty,proto3" json:"empty,omitempty"` Timestamp *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=timestamp,proto3" json:"timestamp,omitempty"` @@ -42,6 +44,13 @@ func (x *MessageWithWKT) Reset() { func (*MessageWithWKT) ProtoMessage() {} +func (x *MessageWithWKT) GetAny() *anypb.Any { + if x != nil { + return x.Any + } + return nil +} + func (x *MessageWithWKT) GetDuration() *durationpb.Duration { if x != nil { return x.Duration @@ -160,6 +169,9 @@ func (m *MessageWithWKT) CloneVT() *MessageWithWKT { } r := new(MessageWithWKT) r.NullValue = m.NullValue + if rhs := m.Any; rhs != nil { + r.Any = rhs.CloneVT() + } if rhs := m.Duration; rhs != nil { r.Duration = rhs.CloneVT() } @@ -222,6 +234,9 @@ func (this *MessageWithWKT) EqualVT(that *MessageWithWKT) bool { } else if this == nil || that == nil { return false } + if !this.Any.EqualVT(that.Any) { + return false + } if !this.Duration.EqualVT(that.Duration) { return false } @@ -471,6 +486,16 @@ func (m *MessageWithWKT) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i-- dAtA[i] = 0x12 } + if m.Any != nil { + size, err := m.Any.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } return len(dAtA) - i, nil } @@ -665,6 +690,16 @@ func (m *MessageWithWKT) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) i-- dAtA[i] = 0x12 } + if m.Any != nil { + size, err := m.Any.MarshalToSizedBufferVTStrict(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } return len(dAtA) - i, nil } @@ -674,6 +709,10 @@ func (m *MessageWithWKT) SizeVT() (n int) { } var l int _ = l + if m.Any != nil { + l = m.Any.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } if m.Duration != nil { l = m.Duration.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) @@ -770,6 +809,42 @@ func (m *MessageWithWKT) UnmarshalVT(dAtA []byte) error { return fmt.Errorf("proto: MessageWithWKT: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Any", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Any == nil { + m.Any = &anypb.Any{} + } + if err := m.Any.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Duration", wireType) @@ -1380,6 +1455,42 @@ func (m *MessageWithWKT) UnmarshalVTUnsafe(dAtA []byte) error { return fmt.Errorf("proto: MessageWithWKT: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Any", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Any == nil { + m.Any = &anypb.Any{} + } + if err := m.Any.UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Duration", wireType) diff --git a/testproto/wkt/wkt.proto b/testproto/wkt/wkt.proto index bb54e894..5e8aedfc 100644 --- a/testproto/wkt/wkt.proto +++ b/testproto/wkt/wkt.proto @@ -1,5 +1,6 @@ syntax = "proto3"; +import "google/protobuf/any.proto"; import "google/protobuf/duration.proto"; import "google/protobuf/empty.proto"; import "google/protobuf/timestamp.proto"; @@ -7,6 +8,7 @@ import "google/protobuf/wrappers.proto"; import "google/protobuf/struct.proto"; message MessageWithWKT { + google.protobuf.Any any = 1; google.protobuf.Duration duration = 2; google.protobuf.Empty empty = 3; google.protobuf.Timestamp timestamp = 5; diff --git a/testproto/wkt/wkt_test.go b/testproto/wkt/wkt_test.go index 7e20d2c6..090d6f09 100644 --- a/testproto/wkt/wkt_test.go +++ b/testproto/wkt/wkt_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/require" + "github.com/aperturerobotics/protobuf-go-lite/types/known/anypb" "github.com/aperturerobotics/protobuf-go-lite/types/known/durationpb" "github.com/aperturerobotics/protobuf-go-lite/types/known/emptypb" "github.com/aperturerobotics/protobuf-go-lite/types/known/timestamppb" @@ -15,7 +16,11 @@ import ( func TestWellKnownTypes(t *testing.T) { dur := durationpb.New(4*time.Hour + 2*time.Second) + anyVal, err := anypb.New(dur, "cool.apps/test-value") + require.NoError(t, err) + m := &MessageWithWKT{ + Any: anyVal, Duration: dur, Empty: &emptypb.Empty{}, Timestamp: timestamppb.Now(), diff --git a/types/known/anypb/any.go b/types/known/anypb/any.go new file mode 100644 index 00000000..ed60bb98 --- /dev/null +++ b/types/known/anypb/any.go @@ -0,0 +1,121 @@ +package anypb + +import ( + protobuf_go_lite "github.com/aperturerobotics/protobuf-go-lite" + "github.com/pkg/errors" +) + +// MessageTypeResolver is an interface for looking up messages. +// +// A compliant implementation must deterministically return the same type +// if no error is encountered. +// +// The [Types] type implements this interface. +type MessageTypeResolver interface { + // FindMessageByURL looks up a message by a URL identifier. + // See documentation on google.protobuf.Any.type_url for the URL format. + // + // Returns the constructor for the message. + // This returns (nil, ErrNotFound) if not found. + FindMessageByURL(url string) (func() protobuf_go_lite.Message, error) +} + +// ErrNotFound is returned if the message type was not found. +var ErrNotFound = errors.New("proto type not found") + +// New marshals src into a new Any instance. +func New(src protobuf_go_lite.Message, typeURL string) (*Any, error) { + dst := new(Any) + if err := dst.MarshalFrom(src, typeURL); err != nil { + return nil, err + } + return dst, nil +} + +// MarshalFrom marshals src into dst as the underlying message +// using the provided marshal options. +// +// If no options are specified, call dst.MarshalFrom instead. +func MarshalFrom(dst *Any, src protobuf_go_lite.Message, typeURL string) error { + if src == nil { + dst.Reset() + return nil + } + b, err := src.MarshalVT() + if err != nil { + return err + } + dst.TypeUrl = typeURL + dst.Value = b + return nil +} + +// UnmarshalTo unmarshals the underlying message from src into dst +// using the provided unmarshal options. +// It reports an error if dst is not of the right message type. +// +// If no options are specified, call src.UnmarshalTo instead. +func UnmarshalTo(src *Any, dst protobuf_go_lite.Message, typeURL string) error { + if src == nil { + dst.Reset() + return nil + } + if !src.MessageIs(typeURL) { + got := typeURL + want := src.GetTypeUrl() + return errors.Errorf("mismatched message type: got %q, want %q", got, want) + } + return dst.UnmarshalVT(src.GetValue()) +} + +// UnmarshalNew unmarshals the underlying message from src into dst, +// which is newly created message using a type resolved from the type URL. +// The message type is resolved according to opt.Resolver, +// which should implement protoregistry.MessageTypeResolver. +// It reports an error if the underlying message type could not be resolved. +// +// If no options are specified, call src.UnmarshalNew instead. +func UnmarshalNew(src *Any, typeURL string, resolver MessageTypeResolver) (dst protobuf_go_lite.Message, err error) { + if src.GetTypeUrl() == "" { + return nil, errors.New("invalid empty type URL") + } + if resolver == nil { + return nil, errors.New("message type resolver cannot be empty") + } + mt, err := resolver.FindMessageByURL(src.GetTypeUrl()) + if err != nil { + if err == ErrNotFound { + return nil, err + } + return nil, errors.Wrapf(err, "could not resolve %q", src.GetTypeUrl()) + } + dst = mt() + if dst == nil { + return nil, ErrNotFound + } + return dst, nil +} + +// MessageIs reports whether the underlying message is of the same type as m. +func (x *Any) MessageIs(typeURL string) bool { + return x.GetTypeUrl() == typeURL +} + +// MarshalFrom marshals m into x as the underlying message. +func (x *Any) MarshalFrom(m protobuf_go_lite.Message, typeURL string) error { + return MarshalFrom(x, m, typeURL) +} + +// UnmarshalTo unmarshals the contents of the underlying message of x into m. +// It resets m before performing the unmarshal operation. +// It reports an error if m is not of the right message type. +func (x *Any) UnmarshalTo(m protobuf_go_lite.Message, typeURL string) error { + return UnmarshalTo(x, m, typeURL) +} + +// UnmarshalNew unmarshals the contents of the underlying message of x into +// a newly allocated message of the specified type. +// It reports an error if the underlying message type could not be resolved. +func (x *Any) UnmarshalNew(typeURL string, resolver MessageTypeResolver) (protobuf_go_lite.Message, error) { + return UnmarshalNew(x, typeURL, resolver) +} diff --git a/types/known/anypb/any.pb.go b/types/known/anypb/any.pb.go new file mode 100644 index 00000000..0fc3248b --- /dev/null +++ b/types/known/anypb/any.pb.go @@ -0,0 +1,575 @@ +// Code generated by protoc-gen-go-lite. DO NOT EDIT. +// protoc-gen-go-lite version: v0.1.2 +// source: github.com/aperturerobotics/protobuf-go-lite/types/known/anypb/any.proto + +package anypb + +import ( + fmt "fmt" + io "io" + unsafe "unsafe" + + protohelpers "github.com/aperturerobotics/protobuf-go-lite/protohelpers" +) + +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// `Any` contains an arbitrary serialized protocol buffer message along with a +// URL that describes the type of the serialized message. +// +// Protobuf library provides support to pack/unpack Any values in the form +// of utility functions or additional generated methods of the Any type. +// +// Example 1: Pack and unpack a message in C++. +// +// Foo foo = ...; +// Any any; +// any.PackFrom(foo); +// ... +// if (any.UnpackTo(&foo)) { +// ... +// } +// +// Example 2: Pack and unpack a message in Java. +// +// Foo foo = ...; +// Any any = Any.pack(foo); +// ... +// if (any.is(Foo.class)) { +// foo = any.unpack(Foo.class); +// } +// // or ... +// if (any.isSameTypeAs(Foo.getDefaultInstance())) { +// foo = any.unpack(Foo.getDefaultInstance()); +// } +// +// Example 3: Pack and unpack a message in Python. +// +// foo = Foo(...) +// any = Any() +// any.Pack(foo) +// ... +// if any.Is(Foo.DESCRIPTOR): +// any.Unpack(foo) +// ... +// +// Example 4: Pack and unpack a message in Go +// +// foo := &pb.Foo{...} +// any, err := anypb.New(foo) +// if err != nil { +// ... +// } +// ... +// foo := &pb.Foo{} +// if err := any.UnmarshalTo(foo); err != nil { +// ... +// } +// +// The pack methods provided by protobuf library will by default use +// 'type.googleapis.com/full.type.name' as the type URL and the unpack +// methods only use the fully qualified type name after the last '/' +// in the type URL, for example "foo.bar.com/x/y.z" will yield type +// name "y.z". +// +// JSON +// ==== +// The JSON representation of an `Any` value uses the regular +// representation of the deserialized, embedded message, with an +// additional field `@type` which contains the type URL. Example: +// +// package google.profile; +// message Person { +// string first_name = 1; +// string last_name = 2; +// } +// +// { +// "@type": "type.googleapis.com/google.profile.Person", +// "firstName": , +// "lastName": +// } +// +// If the embedded message type is well-known and has a custom JSON +// representation, that representation will be embedded adding a field +// `value` which holds the custom JSON in addition to the `@type` +// field. Example (for message [google.protobuf.Duration][]): +// +// { +// "@type": "type.googleapis.com/google.protobuf.Duration", +// "value": "1.212s" +// } +type Any struct { + unknownFields []byte + // A URL/resource name that uniquely identifies the type of the serialized + // protocol buffer message. This string must contain at least + // one "/" character. The last segment of the URL's path must represent + // the fully qualified name of the type (as in + // `path/google.protobuf.Duration`). The name should be in a canonical form + // (e.g., leading "." is not accepted). + // + // In practice, teams usually precompile into the binary all types that they + // expect it to use in the context of Any. However, for URLs which use the + // scheme `http`, `https`, or no scheme, one can optionally set up a type + // server that maps type URLs to message definitions as follows: + // + // - If no scheme is provided, `https` is assumed. + // - An HTTP GET on the URL must yield a [google.protobuf.Type][] + // value in binary format, or produce an error. + // - Applications are allowed to cache lookup results based on the + // URL, or have them precompiled into a binary to avoid any + // lookup. Therefore, binary compatibility needs to be preserved + // on changes to types. (Use versioned type names to manage + // breaking changes.) + // + // Note: this functionality is not currently available in the official + // protobuf release, and it is not used for type URLs beginning with + // type.googleapis.com. As of May 2023, there are no widely used type server + // implementations and no plans to implement one. + // + // Schemes other than `http`, `https` (or the empty scheme) might be + // used with implementation specific semantics. + TypeUrl string `protobuf:"bytes,1,opt,name=type_url,json=typeUrl,proto3" json:"type_url,omitempty"` + // Must be a valid serialized protocol buffer of the above specified type. + Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *Any) Reset() { + *x = Any{} +} + +func (*Any) ProtoMessage() {} + +func (x *Any) GetTypeUrl() string { + if x != nil { + return x.TypeUrl + } + return "" +} + +func (x *Any) GetValue() []byte { + if x != nil { + return x.Value + } + return nil +} + +func (m *Any) CloneVT() *Any { + if m == nil { + return (*Any)(nil) + } + r := new(Any) + r.TypeUrl = m.TypeUrl + if rhs := m.Value; rhs != nil { + tmpBytes := make([]byte, len(rhs)) + copy(tmpBytes, rhs) + r.Value = tmpBytes + } + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *Any) CloneMessageVT() any { + return m.CloneVT() +} + +func (this *Any) EqualVT(that *Any) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.TypeUrl != that.TypeUrl { + return false + } + if string(this.Value) != string(that.Value) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *Any) EqualMessageVT(thatMsg any) bool { + that, ok := thatMsg.(*Any) + if !ok { + return false + } + return this.EqualVT(that) +} +func (m *Any) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Any) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Any) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Value) > 0 { + i -= len(m.Value) + copy(dAtA[i:], m.Value) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Value))) + i-- + dAtA[i] = 0x12 + } + if len(m.TypeUrl) > 0 { + i -= len(m.TypeUrl) + copy(dAtA[i:], m.TypeUrl) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.TypeUrl))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Any) MarshalVTStrict() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Any) MarshalToVTStrict(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVTStrict(dAtA[:size]) +} + +func (m *Any) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Value) > 0 { + i -= len(m.Value) + copy(dAtA[i:], m.Value) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Value))) + i-- + dAtA[i] = 0x12 + } + if len(m.TypeUrl) > 0 { + i -= len(m.TypeUrl) + copy(dAtA[i:], m.TypeUrl) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.TypeUrl))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Any) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.TypeUrl) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + l = len(m.Value) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *Any) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Any: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Any: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TypeUrl", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TypeUrl = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Value = append(m.Value[:0], dAtA[iNdEx:postIndex]...) + if m.Value == nil { + m.Value = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Any) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Any: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Any: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TypeUrl", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.TypeUrl = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Value = dAtA[iNdEx:postIndex] + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} diff --git a/types/known/anypb/any.proto b/types/known/anypb/any.proto new file mode 100644 index 00000000..280f58da --- /dev/null +++ b/types/known/anypb/any.proto @@ -0,0 +1,157 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option go_package = "github.com/aperturerobotics/protobuf-go-lite/types/known/anypb;anypb"; + +// `Any` contains an arbitrary serialized protocol buffer message along with a +// URL that describes the type of the serialized message. +// +// Protobuf library provides support to pack/unpack Any values in the form +// of utility functions or additional generated methods of the Any type. +// +// Example 1: Pack and unpack a message in C++. +// +// Foo foo = ...; +// Any any; +// any.PackFrom(foo); +// ... +// if (any.UnpackTo(&foo)) { +// ... +// } +// +// Example 2: Pack and unpack a message in Java. +// +// Foo foo = ...; +// Any any = Any.pack(foo); +// ... +// if (any.is(Foo.class)) { +// foo = any.unpack(Foo.class); +// } +// // or ... +// if (any.isSameTypeAs(Foo.getDefaultInstance())) { +// foo = any.unpack(Foo.getDefaultInstance()); +// } +// +// Example 3: Pack and unpack a message in Python. +// +// foo = Foo(...) +// any = Any() +// any.Pack(foo) +// ... +// if any.Is(Foo.DESCRIPTOR): +// any.Unpack(foo) +// ... +// +// Example 4: Pack and unpack a message in Go +// +// foo := &pb.Foo{...} +// any, err := anypb.New(foo) +// if err != nil { +// ... +// } +// ... +// foo := &pb.Foo{} +// if err := any.UnmarshalTo(foo); err != nil { +// ... +// } +// +// The pack methods provided by protobuf library will by default use +// 'type.googleapis.com/full.type.name' as the type URL and the unpack +// methods only use the fully qualified type name after the last '/' +// in the type URL, for example "foo.bar.com/x/y.z" will yield type +// name "y.z". +// +// JSON +// ==== +// The JSON representation of an `Any` value uses the regular +// representation of the deserialized, embedded message, with an +// additional field `@type` which contains the type URL. Example: +// +// package google.profile; +// message Person { +// string first_name = 1; +// string last_name = 2; +// } +// +// { +// "@type": "type.googleapis.com/google.profile.Person", +// "firstName": , +// "lastName": +// } +// +// If the embedded message type is well-known and has a custom JSON +// representation, that representation will be embedded adding a field +// `value` which holds the custom JSON in addition to the `@type` +// field. Example (for message [google.protobuf.Duration][]): +// +// { +// "@type": "type.googleapis.com/google.protobuf.Duration", +// "value": "1.212s" +// } +// +message Any { + // A URL/resource name that uniquely identifies the type of the serialized + // protocol buffer message. This string must contain at least + // one "/" character. The last segment of the URL's path must represent + // the fully qualified name of the type (as in + // `path/google.protobuf.Duration`). The name should be in a canonical form + // (e.g., leading "." is not accepted). + // + // In practice, teams usually precompile into the binary all types that they + // expect it to use in the context of Any. However, for URLs which use the + // scheme `http`, `https`, or no scheme, one can optionally set up a type + // server that maps type URLs to message definitions as follows: + // + // * If no scheme is provided, `https` is assumed. + // * An HTTP GET on the URL must yield a [google.protobuf.Type][] + // value in binary format, or produce an error. + // * Applications are allowed to cache lookup results based on the + // URL, or have them precompiled into a binary to avoid any + // lookup. Therefore, binary compatibility needs to be preserved + // on changes to types. (Use versioned type names to manage + // breaking changes.) + // + // Note: this functionality is not currently available in the official + // protobuf release, and it is not used for type URLs beginning with + // type.googleapis.com. As of May 2023, there are no widely used type server + // implementations and no plans to implement one. + // + // Schemes other than `http`, `https` (or the empty scheme) might be + // used with implementation specific semantics. + // + string type_url = 1; + + // Must be a valid serialized protocol buffer of the above specified type. + bytes value = 2; +} diff --git a/types/known/anypb/any_test.go b/types/known/anypb/any_test.go new file mode 100644 index 00000000..e2704b87 --- /dev/null +++ b/types/known/anypb/any_test.go @@ -0,0 +1,118 @@ +// Copyright 2020 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 anypb_test + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/testing/protocmp" + + apb "google.golang.org/protobuf/types/known/anypb" + epb "google.golang.org/protobuf/types/known/emptypb" + wpb "google.golang.org/protobuf/types/known/wrapperspb" +) + +func mustMarshal(m proto.Message) []byte { + b, err := proto.MarshalOptions{AllowPartial: true, Deterministic: true}.Marshal(m) + if err != nil { + panic(err) + } + return b +} + +func TestMessage(t *testing.T) { + tests := []struct { + inAny *apb.Any + inTarget proto.Message + wantIs bool + wantName protoreflect.FullName + }{{ + inAny: nil, + inTarget: nil, + wantIs: false, + wantName: "", + }, { + inAny: new(apb.Any), + inTarget: nil, + wantIs: false, + wantName: "", + }} + + for _, tt := range tests { + gotIs := tt.inAny.MessageIs(tt.inTarget) + if gotIs != tt.wantIs { + t.Errorf("MessageIs(%v, %v) = %v, want %v", tt.inAny, tt.inTarget, gotIs, tt.wantIs) + } + gotName := tt.inAny.MessageName() + if gotName != tt.wantName { + t.Errorf("MessageName(%v) = %v, want %v", tt.inAny, gotName, tt.wantName) + } + } +} + +func TestRoundtrip(t *testing.T) { + tests := []struct { + msg proto.Message + any *apb.Any + }{{ + msg: &wpb.StringValue{Value: ""}, + any: &apb.Any{ + TypeUrl: "type.googleapis.com/google.protobuf.StringValue", + }, + }, { + msg: wpb.String("hello, world"), + any: &apb.Any{ + TypeUrl: "type.googleapis.com/google.protobuf.StringValue", + Value: mustMarshal(wpb.String("hello, world")), + }, + }, { + msg: &apb.Any{ + TypeUrl: "type.googleapis.com/google.protobuf.StringValue", + Value: mustMarshal(wpb.String("hello, world")), + }, + any: &apb.Any{ + TypeUrl: "type.googleapis.com/google.protobuf.Any", + Value: mustMarshal(&apb.Any{ + TypeUrl: "type.googleapis.com/google.protobuf.StringValue", + Value: mustMarshal(wpb.String("hello, world")), + }), + }, + }} + + for _, tt := range tests { + // Unmarshal to the wrong message type. + var empty epb.Empty + if err := tt.any.UnmarshalTo(&empty); err == nil { + t.Errorf("UnmarshalTo(empty) = nil, want non-nil") + } + + gotAny := new(apb.Any) + if err := gotAny.MarshalFrom(tt.msg); err != nil { + t.Errorf("MarshalFrom() error: %v", err) + } + if diff := cmp.Diff(tt.any, gotAny, protocmp.Transform()); diff != "" { + t.Errorf("MarshalFrom() output mismatch (-want +got):\n%s", diff) + } + + gotPB := tt.msg.ProtoReflect().New().Interface() + if err := tt.any.UnmarshalTo(gotPB); err != nil { + t.Errorf("UnmarshalTo() error: %v", err) + } + if diff := cmp.Diff(tt.msg, gotPB, protocmp.Transform()); diff != "" { + t.Errorf("UnmarshalTo() output mismatch (-want +got):\n%s", diff) + } + + gotPB, err := tt.any.UnmarshalNew() + if err != nil { + t.Errorf("UnmarshalNew() error: %v", err) + } + if diff := cmp.Diff(tt.msg, gotPB, protocmp.Transform()); diff != "" { + t.Errorf("UnmarshalNew() output mismatch (-want +got):\n%s", diff) + } + } +}