-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This provides a more reasonable API for obtaining a FileDescriptorProto and DescriptorProto for a given proto.Message — a process that is currently possible (but undocumented) using the public proto API. The major use case for obtaining a DescriptorProto is to decode custom message options, which are encoded as extensions on the MessageOptions submessage. (See https://developers.google.com/protocol-buffers/docs/proto#customoptions.) Fixes #179. PiperOrigin-RevId: 139356528
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
// Go support for Protocol Buffers - Google's data interchange format | ||
// | ||
// Copyright 2016 The Go Authors. All rights reserved. | ||
// https://github.com/golang/protobuf | ||
// | ||
// 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. | ||
|
||
// Package descriptor provides functions for obtaining protocol buffer | ||
// descriptors for generated Go types. | ||
// | ||
// These functions cannot go in package proto because they depend on the | ||
// generated protobuf descriptor messages, which themselves depend on proto. | ||
package descriptor | ||
|
||
import ( | ||
"bytes" | ||
"compress/gzip" | ||
"fmt" | ||
"io/ioutil" | ||
|
||
"github.com/golang/protobuf/proto" | ||
"google.golang.org/genproto/protobuf" | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
bcmills
Contributor
|
||
) | ||
|
||
// extractFile extracts a FileDescriptorProto from a gzip'd buffer. | ||
func extractFile(gz []byte) (*protobuf.FileDescriptorProto, error) { | ||
r, err := gzip.NewReader(bytes.NewReader(gz)) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to open gzip reader: %v", err) | ||
} | ||
defer r.Close() | ||
|
||
b, err := ioutil.ReadAll(r) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to uncompress descriptor: %v", err) | ||
} | ||
|
||
fd := new(protobuf.FileDescriptorProto) | ||
if err := proto.Unmarshal(b, fd); err != nil { | ||
return nil, fmt.Errorf("malformed FileDescriptorProto: %v", err) | ||
} | ||
|
||
return fd, nil | ||
} | ||
|
||
// Message is a proto.Message with a method to return its descriptor. | ||
// | ||
// Message types generated by the protocol compiler always satisfy | ||
// the Message interface. | ||
type Message interface { | ||
proto.Message | ||
Descriptor() ([]byte, []int) | ||
} | ||
|
||
// ForMessage returns a FileDescriptorProto and a DescriptorProto from within it | ||
// describing the given message. | ||
func ForMessage(msg Message) (fd *protobuf.FileDescriptorProto, md *protobuf.DescriptorProto) { | ||
gz, path := msg.Descriptor() | ||
fd, err := extractFile(gz) | ||
if err != nil { | ||
panic(fmt.Sprintf("invalid FileDescriptorProto for %T: %v", msg, err)) | ||
} | ||
|
||
md = fd.MessageType[path[0]] | ||
for _, i := range path[1:] { | ||
md = md.NestedType[i] | ||
} | ||
return fd, md | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package descriptor_test | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/golang/protobuf/descriptor" | ||
tpb "github.com/golang/protobuf/proto/testdata" | ||
"google.golang.org/genproto/protobuf" | ||
) | ||
|
||
func TestMessage(t *testing.T) { | ||
var msg *protobuf.DescriptorProto | ||
fd, md := descriptor.ForMessage(msg) | ||
if pkg, want := fd.GetPackage(), "google.protobuf"; pkg != want { | ||
t.Errorf("descriptor.ForMessage(%T).GetPackage() = %q; want %q", msg, pkg, want) | ||
} | ||
if name, want := md.GetName(), "DescriptorProto"; name != want { | ||
t.Fatalf("descriptor.ForMessage(%T).GetName() = %q; want %q", msg, name, want) | ||
} | ||
} | ||
|
||
func Example_Options() { | ||
var msg *tpb.MyMessageSet | ||
_, md := descriptor.ForMessage(msg) | ||
if md.GetOptions().GetMessageSetWireFormat() { | ||
fmt.Printf("%v uses option message_set_wire_format.\n", md.GetName()) | ||
} | ||
|
||
// Output: | ||
// MyMessageSet uses option message_set_wire_format. | ||
} |
Are we introducing dependencies into golang/protobuf now?