blob: 5491afda1e8f8a9f730d26e57e5bdada90e4a20b [file] [log] [blame]
package desc
import (
"fmt"
"github.com/bufbuild/protocompile/protoutil"
"google.golang.org/protobuf/reflect/protoreflect"
)
// DescriptorWrapper wraps a protoreflect.Descriptor. All of the Descriptor
// implementations in this package implement this interface. This can be
// used to recover the underlying descriptor. Each descriptor type in this
// package also provides a strongly-typed form of this method, such as the
// following method for *FileDescriptor:
//
// UnwrapFile() protoreflect.FileDescriptor
type DescriptorWrapper interface {
Unwrap() protoreflect.Descriptor
}
// WrapDescriptor wraps the given descriptor, returning a desc.Descriptor
// value that represents the same element.
func WrapDescriptor(d protoreflect.Descriptor) (Descriptor, error) {
return wrapDescriptor(d, mapCache{})
}
func wrapDescriptor(d protoreflect.Descriptor, cache descriptorCache) (Descriptor, error) {
switch d := d.(type) {
case protoreflect.FileDescriptor:
return wrapFile(d, cache)
case protoreflect.MessageDescriptor:
return wrapMessage(d, cache)
case protoreflect.FieldDescriptor:
return wrapField(d, cache)
case protoreflect.OneofDescriptor:
return wrapOneOf(d, cache)
case protoreflect.EnumDescriptor:
return wrapEnum(d, cache)
case protoreflect.EnumValueDescriptor:
return wrapEnumValue(d, cache)
case protoreflect.ServiceDescriptor:
return wrapService(d, cache)
case protoreflect.MethodDescriptor:
return wrapMethod(d, cache)
default:
return nil, fmt.Errorf("unknown descriptor type: %T", d)
}
}
// WrapFiles wraps the given file descriptors, returning a slice of *desc.FileDescriptor
// values that represent the same files.
func WrapFiles(d []protoreflect.FileDescriptor) ([]*FileDescriptor, error) {
cache := mapCache{}
results := make([]*FileDescriptor, len(d))
for i := range d {
var err error
results[i], err = wrapFile(d[i], cache)
if err != nil {
return nil, err
}
}
return results, nil
}
// WrapFile wraps the given file descriptor, returning a *desc.FileDescriptor
// value that represents the same file.
func WrapFile(d protoreflect.FileDescriptor) (*FileDescriptor, error) {
return wrapFile(d, mapCache{})
}
func wrapFile(d protoreflect.FileDescriptor, cache descriptorCache) (*FileDescriptor, error) {
if res := cache.get(d); res != nil {
return res.(*FileDescriptor), nil
}
fdp := protoutil.ProtoFromFileDescriptor(d)
return convertFile(d, fdp, cache)
}
// WrapMessage wraps the given message descriptor, returning a *desc.MessageDescriptor
// value that represents the same message.
func WrapMessage(d protoreflect.MessageDescriptor) (*MessageDescriptor, error) {
return wrapMessage(d, mapCache{})
}
func wrapMessage(d protoreflect.MessageDescriptor, cache descriptorCache) (*MessageDescriptor, error) {
parent, err := wrapDescriptor(d.Parent(), cache)
if err != nil {
return nil, err
}
switch p := parent.(type) {
case *FileDescriptor:
return p.messages[d.Index()], nil
case *MessageDescriptor:
return p.nested[d.Index()], nil
default:
return nil, fmt.Errorf("message has unexpected parent type: %T", parent)
}
}
// WrapField wraps the given field descriptor, returning a *desc.FieldDescriptor
// value that represents the same field.
func WrapField(d protoreflect.FieldDescriptor) (*FieldDescriptor, error) {
return wrapField(d, mapCache{})
}
func wrapField(d protoreflect.FieldDescriptor, cache descriptorCache) (*FieldDescriptor, error) {
parent, err := wrapDescriptor(d.Parent(), cache)
if err != nil {
return nil, err
}
switch p := parent.(type) {
case *FileDescriptor:
return p.extensions[d.Index()], nil
case *MessageDescriptor:
if d.IsExtension() {
return p.extensions[d.Index()], nil
}
return p.fields[d.Index()], nil
default:
return nil, fmt.Errorf("field has unexpected parent type: %T", parent)
}
}
// WrapOneOf wraps the given oneof descriptor, returning a *desc.OneOfDescriptor
// value that represents the same oneof.
func WrapOneOf(d protoreflect.OneofDescriptor) (*OneOfDescriptor, error) {
return wrapOneOf(d, mapCache{})
}
func wrapOneOf(d protoreflect.OneofDescriptor, cache descriptorCache) (*OneOfDescriptor, error) {
parent, err := wrapDescriptor(d.Parent(), cache)
if err != nil {
return nil, err
}
if p, ok := parent.(*MessageDescriptor); ok {
return p.oneOfs[d.Index()], nil
}
return nil, fmt.Errorf("oneof has unexpected parent type: %T", parent)
}
// WrapEnum wraps the given enum descriptor, returning a *desc.EnumDescriptor
// value that represents the same enum.
func WrapEnum(d protoreflect.EnumDescriptor) (*EnumDescriptor, error) {
return wrapEnum(d, mapCache{})
}
func wrapEnum(d protoreflect.EnumDescriptor, cache descriptorCache) (*EnumDescriptor, error) {
parent, err := wrapDescriptor(d.Parent(), cache)
if err != nil {
return nil, err
}
switch p := parent.(type) {
case *FileDescriptor:
return p.enums[d.Index()], nil
case *MessageDescriptor:
return p.enums[d.Index()], nil
default:
return nil, fmt.Errorf("enum has unexpected parent type: %T", parent)
}
}
// WrapEnumValue wraps the given enum value descriptor, returning a *desc.EnumValueDescriptor
// value that represents the same enum value.
func WrapEnumValue(d protoreflect.EnumValueDescriptor) (*EnumValueDescriptor, error) {
return wrapEnumValue(d, mapCache{})
}
func wrapEnumValue(d protoreflect.EnumValueDescriptor, cache descriptorCache) (*EnumValueDescriptor, error) {
parent, err := wrapDescriptor(d.Parent(), cache)
if err != nil {
return nil, err
}
if p, ok := parent.(*EnumDescriptor); ok {
return p.values[d.Index()], nil
}
return nil, fmt.Errorf("enum value has unexpected parent type: %T", parent)
}
// WrapService wraps the given service descriptor, returning a *desc.ServiceDescriptor
// value that represents the same service.
func WrapService(d protoreflect.ServiceDescriptor) (*ServiceDescriptor, error) {
return wrapService(d, mapCache{})
}
func wrapService(d protoreflect.ServiceDescriptor, cache descriptorCache) (*ServiceDescriptor, error) {
parent, err := wrapDescriptor(d.Parent(), cache)
if err != nil {
return nil, err
}
if p, ok := parent.(*FileDescriptor); ok {
return p.services[d.Index()], nil
}
return nil, fmt.Errorf("service has unexpected parent type: %T", parent)
}
// WrapMethod wraps the given method descriptor, returning a *desc.MethodDescriptor
// value that represents the same method.
func WrapMethod(d protoreflect.MethodDescriptor) (*MethodDescriptor, error) {
return wrapMethod(d, mapCache{})
}
func wrapMethod(d protoreflect.MethodDescriptor, cache descriptorCache) (*MethodDescriptor, error) {
parent, err := wrapDescriptor(d.Parent(), cache)
if err != nil {
return nil, err
}
if p, ok := parent.(*ServiceDescriptor); ok {
return p.methods[d.Index()], nil
}
return nil, fmt.Errorf("method has unexpected parent type: %T", parent)
}