Don Newton | 98fd881 | 2019-09-23 15:15:02 -0400 | [diff] [blame] | 1 | // Go support for Protocol Buffers - Google's data interchange format |
| 2 | // |
| 3 | // Copyright 2016 The Go Authors. All rights reserved. |
| 4 | // https://github.com/golang/protobuf |
| 5 | // |
| 6 | // Redistribution and use in source and binary forms, with or without |
| 7 | // modification, are permitted provided that the following conditions are |
| 8 | // met: |
| 9 | // |
| 10 | // * Redistributions of source code must retain the above copyright |
| 11 | // notice, this list of conditions and the following disclaimer. |
| 12 | // * Redistributions in binary form must reproduce the above |
| 13 | // copyright notice, this list of conditions and the following disclaimer |
| 14 | // in the documentation and/or other materials provided with the |
| 15 | // distribution. |
| 16 | // * Neither the name of Google Inc. nor the names of its |
| 17 | // contributors may be used to endorse or promote products derived from |
| 18 | // this software without specific prior written permission. |
| 19 | // |
| 20 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 21 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 22 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 23 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 24 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 25 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 26 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 27 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 28 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 29 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 30 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 31 | |
| 32 | package ptypes |
| 33 | |
| 34 | // This file implements functions to marshal proto.Message to/from |
| 35 | // google.protobuf.Any message. |
| 36 | |
| 37 | import ( |
| 38 | "fmt" |
| 39 | "reflect" |
| 40 | "strings" |
| 41 | |
| 42 | "github.com/golang/protobuf/proto" |
| 43 | "github.com/golang/protobuf/ptypes/any" |
| 44 | ) |
| 45 | |
| 46 | const googleApis = "type.googleapis.com/" |
| 47 | |
| 48 | // AnyMessageName returns the name of the message contained in a google.protobuf.Any message. |
| 49 | // |
| 50 | // Note that regular type assertions should be done using the Is |
| 51 | // function. AnyMessageName is provided for less common use cases like filtering a |
| 52 | // sequence of Any messages based on a set of allowed message type names. |
| 53 | func AnyMessageName(any *any.Any) (string, error) { |
| 54 | if any == nil { |
| 55 | return "", fmt.Errorf("message is nil") |
| 56 | } |
| 57 | slash := strings.LastIndex(any.TypeUrl, "/") |
| 58 | if slash < 0 { |
| 59 | return "", fmt.Errorf("message type url %q is invalid", any.TypeUrl) |
| 60 | } |
| 61 | return any.TypeUrl[slash+1:], nil |
| 62 | } |
| 63 | |
| 64 | // MarshalAny takes the protocol buffer and encodes it into google.protobuf.Any. |
| 65 | func MarshalAny(pb proto.Message) (*any.Any, error) { |
| 66 | value, err := proto.Marshal(pb) |
| 67 | if err != nil { |
| 68 | return nil, err |
| 69 | } |
| 70 | return &any.Any{TypeUrl: googleApis + proto.MessageName(pb), Value: value}, nil |
| 71 | } |
| 72 | |
| 73 | // DynamicAny is a value that can be passed to UnmarshalAny to automatically |
| 74 | // allocate a proto.Message for the type specified in a google.protobuf.Any |
| 75 | // message. The allocated message is stored in the embedded proto.Message. |
| 76 | // |
| 77 | // Example: |
| 78 | // |
| 79 | // var x ptypes.DynamicAny |
| 80 | // if err := ptypes.UnmarshalAny(a, &x); err != nil { ... } |
| 81 | // fmt.Printf("unmarshaled message: %v", x.Message) |
| 82 | type DynamicAny struct { |
| 83 | proto.Message |
| 84 | } |
| 85 | |
| 86 | // Empty returns a new proto.Message of the type specified in a |
| 87 | // google.protobuf.Any message. It returns an error if corresponding message |
| 88 | // type isn't linked in. |
| 89 | func Empty(any *any.Any) (proto.Message, error) { |
| 90 | aname, err := AnyMessageName(any) |
| 91 | if err != nil { |
| 92 | return nil, err |
| 93 | } |
| 94 | |
| 95 | t := proto.MessageType(aname) |
| 96 | if t == nil { |
| 97 | return nil, fmt.Errorf("any: message type %q isn't linked in", aname) |
| 98 | } |
| 99 | return reflect.New(t.Elem()).Interface().(proto.Message), nil |
| 100 | } |
| 101 | |
| 102 | // UnmarshalAny parses the protocol buffer representation in a google.protobuf.Any |
| 103 | // message and places the decoded result in pb. It returns an error if type of |
| 104 | // contents of Any message does not match type of pb message. |
| 105 | // |
| 106 | // pb can be a proto.Message, or a *DynamicAny. |
| 107 | func UnmarshalAny(any *any.Any, pb proto.Message) error { |
| 108 | if d, ok := pb.(*DynamicAny); ok { |
| 109 | if d.Message == nil { |
| 110 | var err error |
| 111 | d.Message, err = Empty(any) |
| 112 | if err != nil { |
| 113 | return err |
| 114 | } |
| 115 | } |
| 116 | return UnmarshalAny(any, d.Message) |
| 117 | } |
| 118 | |
| 119 | aname, err := AnyMessageName(any) |
| 120 | if err != nil { |
| 121 | return err |
| 122 | } |
| 123 | |
| 124 | mname := proto.MessageName(pb) |
| 125 | if aname != mname { |
| 126 | return fmt.Errorf("mismatched message type: got %q want %q", aname, mname) |
| 127 | } |
| 128 | return proto.Unmarshal(any.Value, pb) |
| 129 | } |
| 130 | |
| 131 | // Is returns true if any value contains a given message type. |
| 132 | func Is(any *any.Any, pb proto.Message) bool { |
| 133 | // The following is equivalent to AnyMessageName(any) == proto.MessageName(pb), |
| 134 | // but it avoids scanning TypeUrl for the slash. |
| 135 | if any == nil { |
| 136 | return false |
| 137 | } |
| 138 | name := proto.MessageName(pb) |
| 139 | prefix := len(any.TypeUrl) - len(name) |
| 140 | return prefix >= 1 && any.TypeUrl[prefix-1] == '/' && any.TypeUrl[prefix:] == name |
| 141 | } |