github.com/cosmos/cosmos-proto@v1.0.0-beta.3/anyutil/any.go (about)

     1  // Protocol Buffers - Google's data interchange format
     2  // Copyright 2008 Google Inc.  All rights reserved.
     3  // https://developers.google.com/protocol-buffers/
     4  //
     5  // Redistribution and use in source and binary forms, with or without
     6  // modification, are permitted provided that the following conditions are
     7  // met:
     8  //
     9  //     * Redistributions of source code must retain the above copyright
    10  // notice, this list of conditions and the following disclaimer.
    11  //     * Redistributions in binary form must reproduce the above
    12  // copyright notice, this list of conditions and the following disclaimer
    13  // in the documentation and/or other materials provided with the
    14  // distribution.
    15  //     * Neither the name of Google Inc. nor the names of its
    16  // contributors may be used to endorse or promote products derived from
    17  // this software without specific prior written permission.
    18  //
    19  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    20  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    21  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    22  // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    23  // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    24  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    25  // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    26  // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    27  // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    28  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    29  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    30  
    31  package anyutil
    32  
    33  import (
    34  	"fmt"
    35  	"strings"
    36  
    37  	"google.golang.org/protobuf/proto"
    38  	"google.golang.org/protobuf/reflect/protodesc"
    39  	"google.golang.org/protobuf/reflect/protoreflect"
    40  	"google.golang.org/protobuf/reflect/protoregistry"
    41  	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
    42  	"google.golang.org/protobuf/types/dynamicpb"
    43  	"google.golang.org/protobuf/types/known/anypb"
    44  )
    45  
    46  // New marshals src into a new Any instance.
    47  func New(src proto.Message) (*anypb.Any, error) {
    48  	dst := new(anypb.Any)
    49  	if err := MarshalFrom(dst, src, proto.MarshalOptions{}); err != nil {
    50  		return nil, err
    51  	}
    52  	return dst, nil
    53  }
    54  
    55  // MarshalFrom marshals src into dst as the underlying message
    56  // using the provided marshal options.
    57  //
    58  // If no options are specified, call dst.MarshalFrom instead.
    59  func MarshalFrom(dst *anypb.Any, src proto.Message, opts proto.MarshalOptions) error {
    60  	if src == nil {
    61  		return protoimpl.X.NewError("invalid nil source message")
    62  	}
    63  	b, err := opts.Marshal(src)
    64  	if err != nil {
    65  		return err
    66  	}
    67  	dst.TypeUrl = "/" + string(src.ProtoReflect().Descriptor().FullName())
    68  	dst.Value = b
    69  	return nil
    70  }
    71  
    72  // Unpack unpacks the message inside an any, first using the provided
    73  // typeResolver (defaults to protoregistry.GlobalTypes), and if that fails,
    74  // then using the provided fileResolver (defaults to protoregistry.GlobalFiles)
    75  // with dynamicpb.
    76  func Unpack(any *anypb.Any, fileResolver protodesc.Resolver, typeResolver protoregistry.MessageTypeResolver) (proto.Message, error) {
    77  	if typeResolver == nil {
    78  		typeResolver = protoregistry.GlobalTypes
    79  	}
    80  
    81  	url := any.TypeUrl
    82  	typ, err := typeResolver.FindMessageByURL(url)
    83  	if err == protoregistry.NotFound {
    84  		if fileResolver == nil {
    85  			fileResolver = protoregistry.GlobalFiles
    86  		}
    87  
    88  		// If the proto v2 registry doesn't have this message, then we use
    89  		// protoFiles (which can e.g. be initialized to gogo's MergedRegistry)
    90  		// to retrieve the message descriptor, and then use dynamicpb on that
    91  		// message descriptor to create a proto.Message
    92  		typeURL := strings.TrimPrefix(any.TypeUrl, "/")
    93  
    94  		msgDesc, err := fileResolver.FindDescriptorByName(protoreflect.FullName(typeURL))
    95  		if err != nil {
    96  			return nil, fmt.Errorf("protoFiles does not have descriptor %s: %w", any.TypeUrl, err)
    97  		}
    98  
    99  		typ = dynamicpb.NewMessageType(msgDesc.(protoreflect.MessageDescriptor))
   100  
   101  	} else if err != nil {
   102  		return nil, err
   103  	}
   104  
   105  	packedMsg := typ.New().Interface()
   106  	err = any.UnmarshalTo(packedMsg)
   107  	if err != nil {
   108  		return nil, fmt.Errorf("cannot unmarshal msg %s: %w", any.TypeUrl, err)
   109  	}
   110  
   111  	return packedMsg, nil
   112  }