|
28 | 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
29 | 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
30 | 30 |
|
31 | | -package any |
| 31 | +package anyutil |
32 | 32 |
|
33 | 33 | import ( |
| 34 | + "fmt" |
| 35 | + "strings" |
| 36 | + |
34 | 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" |
35 | 41 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" |
| 42 | + "google.golang.org/protobuf/types/dynamicpb" |
36 | 43 | "google.golang.org/protobuf/types/known/anypb" |
37 | 44 | ) |
38 | 45 |
|
@@ -61,3 +68,45 @@ func MarshalFrom(dst *anypb.Any, src proto.Message, opts proto.MarshalOptions) e |
61 | 68 | dst.Value = b |
62 | 69 | return nil |
63 | 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 | +} |
0 commit comments