any.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. // Copyright 2016 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package ptypes
  5. // This file implements functions to marshal proto.Message to/from
  6. // google.protobuf.Any message.
  7. import (
  8. "fmt"
  9. "reflect"
  10. "strings"
  11. "github.com/golang/protobuf/proto"
  12. "github.com/golang/protobuf/ptypes/any"
  13. )
  14. const googleApis = "type.googleapis.com/"
  15. // AnyMessageName returns the name of the message contained in a google.protobuf.Any message.
  16. //
  17. // Note that regular type assertions should be done using the Is
  18. // function. AnyMessageName is provided for less common use cases like filtering a
  19. // sequence of Any messages based on a set of allowed message type names.
  20. func AnyMessageName(any *any.Any) (string, error) {
  21. if any == nil {
  22. return "", fmt.Errorf("message is nil")
  23. }
  24. slash := strings.LastIndex(any.TypeUrl, "/")
  25. if slash < 0 {
  26. return "", fmt.Errorf("message type url %q is invalid", any.TypeUrl)
  27. }
  28. return any.TypeUrl[slash+1:], nil
  29. }
  30. // MarshalAny takes the protocol buffer and encodes it into google.protobuf.Any.
  31. func MarshalAny(pb proto.Message) (*any.Any, error) {
  32. value, err := proto.Marshal(pb)
  33. if err != nil {
  34. return nil, err
  35. }
  36. return &any.Any{TypeUrl: googleApis + proto.MessageName(pb), Value: value}, nil
  37. }
  38. // DynamicAny is a value that can be passed to UnmarshalAny to automatically
  39. // allocate a proto.Message for the type specified in a google.protobuf.Any
  40. // message. The allocated message is stored in the embedded proto.Message.
  41. //
  42. // Example:
  43. //
  44. // var x ptypes.DynamicAny
  45. // if err := ptypes.UnmarshalAny(a, &x); err != nil { ... }
  46. // fmt.Printf("unmarshaled message: %v", x.Message)
  47. type DynamicAny struct {
  48. proto.Message
  49. }
  50. // Empty returns a new proto.Message of the type specified in a
  51. // google.protobuf.Any message. It returns an error if corresponding message
  52. // type isn't linked in.
  53. func Empty(any *any.Any) (proto.Message, error) {
  54. aname, err := AnyMessageName(any)
  55. if err != nil {
  56. return nil, err
  57. }
  58. t := proto.MessageType(aname)
  59. if t == nil {
  60. return nil, fmt.Errorf("any: message type %q isn't linked in", aname)
  61. }
  62. return reflect.New(t.Elem()).Interface().(proto.Message), nil
  63. }
  64. // UnmarshalAny parses the protocol buffer representation in a google.protobuf.Any
  65. // message and places the decoded result in pb. It returns an error if type of
  66. // contents of Any message does not match type of pb message.
  67. //
  68. // pb can be a proto.Message, or a *DynamicAny.
  69. func UnmarshalAny(any *any.Any, pb proto.Message) error {
  70. if d, ok := pb.(*DynamicAny); ok {
  71. if d.Message == nil {
  72. var err error
  73. d.Message, err = Empty(any)
  74. if err != nil {
  75. return err
  76. }
  77. }
  78. return UnmarshalAny(any, d.Message)
  79. }
  80. aname, err := AnyMessageName(any)
  81. if err != nil {
  82. return err
  83. }
  84. mname := proto.MessageName(pb)
  85. if aname != mname {
  86. return fmt.Errorf("mismatched message type: got %q want %q", aname, mname)
  87. }
  88. return proto.Unmarshal(any.Value, pb)
  89. }
  90. // Is returns true if any value contains a given message type.
  91. func Is(any *any.Any, pb proto.Message) bool {
  92. // The following is equivalent to AnyMessageName(any) == proto.MessageName(pb),
  93. // but it avoids scanning TypeUrl for the slash.
  94. if any == nil {
  95. return false
  96. }
  97. name := proto.MessageName(pb)
  98. prefix := len(any.TypeUrl) - len(name)
  99. return prefix >= 1 && any.TypeUrl[prefix-1] == '/' && any.TypeUrl[prefix:] == name
  100. }