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) }