| package desc |
| |
| import ( |
| "bytes" |
| "fmt" |
| "sort" |
| "strconv" |
| "strings" |
| "unicode/utf8" |
| |
| "github.com/golang/protobuf/proto" |
| dpb "github.com/golang/protobuf/protoc-gen-go/descriptor" |
| |
| "github.com/jhump/protoreflect/desc/internal" |
| ) |
| |
| // Descriptor is the common interface implemented by all descriptor objects. |
| type Descriptor interface { |
| // GetName returns the name of the object described by the descriptor. This will |
| // be a base name that does not include enclosing message names or the package name. |
| // For file descriptors, this indicates the path and name to the described file. |
| GetName() string |
| // GetFullyQualifiedName returns the fully-qualified name of the object described by |
| // the descriptor. This will include the package name and any enclosing message names. |
| // For file descriptors, this returns the path and name to the described file (same as |
| // GetName). |
| GetFullyQualifiedName() string |
| // GetParent returns the enclosing element in a proto source file. If the described |
| // object is a top-level object, this returns the file descriptor. Otherwise, it returns |
| // the element in which the described object was declared. File descriptors have no |
| // parent and return nil. |
| GetParent() Descriptor |
| // GetFile returns the file descriptor in which this element was declared. File |
| // descriptors return themselves. |
| GetFile() *FileDescriptor |
| // GetOptions returns the options proto containing options for the described element. |
| GetOptions() proto.Message |
| // GetSourceInfo returns any source code information that was present in the file |
| // descriptor. Source code info is optional. If no source code info is available for |
| // the element (including if there is none at all in the file descriptor) then this |
| // returns nil |
| GetSourceInfo() *dpb.SourceCodeInfo_Location |
| // AsProto returns the underlying descriptor proto for this descriptor. |
| AsProto() proto.Message |
| } |
| |
| type sourceInfoRecomputeFunc = internal.SourceInfoComputeFunc |
| |
| // FileDescriptor describes a proto source file. |
| type FileDescriptor struct { |
| proto *dpb.FileDescriptorProto |
| symbols map[string]Descriptor |
| deps []*FileDescriptor |
| publicDeps []*FileDescriptor |
| weakDeps []*FileDescriptor |
| messages []*MessageDescriptor |
| enums []*EnumDescriptor |
| extensions []*FieldDescriptor |
| services []*ServiceDescriptor |
| fieldIndex map[string]map[int32]*FieldDescriptor |
| isProto3 bool |
| sourceInfo internal.SourceInfoMap |
| sourceInfoRecomputeFunc |
| } |
| |
| func (fd *FileDescriptor) recomputeSourceInfo() { |
| internal.PopulateSourceInfoMap(fd.proto, fd.sourceInfo) |
| } |
| |
| func (fd *FileDescriptor) registerField(field *FieldDescriptor) { |
| fields := fd.fieldIndex[field.owner.GetFullyQualifiedName()] |
| if fields == nil { |
| fields = map[int32]*FieldDescriptor{} |
| fd.fieldIndex[field.owner.GetFullyQualifiedName()] = fields |
| } |
| fields[field.GetNumber()] = field |
| } |
| |
| // GetName returns the name of the file, as it was given to the protoc invocation |
| // to compile it, possibly including path (relative to a directory in the proto |
| // import path). |
| func (fd *FileDescriptor) GetName() string { |
| return fd.proto.GetName() |
| } |
| |
| // GetFullyQualifiedName returns the name of the file, same as GetName. It is |
| // present to satisfy the Descriptor interface. |
| func (fd *FileDescriptor) GetFullyQualifiedName() string { |
| return fd.proto.GetName() |
| } |
| |
| // GetPackage returns the name of the package declared in the file. |
| func (fd *FileDescriptor) GetPackage() string { |
| return fd.proto.GetPackage() |
| } |
| |
| // GetParent always returns nil: files are the root of descriptor hierarchies. |
| // Is it present to satisfy the Descriptor interface. |
| func (fd *FileDescriptor) GetParent() Descriptor { |
| return nil |
| } |
| |
| // GetFile returns the receiver, which is a file descriptor. This is present |
| // to satisfy the Descriptor interface. |
| func (fd *FileDescriptor) GetFile() *FileDescriptor { |
| return fd |
| } |
| |
| // GetOptions returns the file's options. Most usages will be more interested |
| // in GetFileOptions, which has a concrete return type. This generic version |
| // is present to satisfy the Descriptor interface. |
| func (fd *FileDescriptor) GetOptions() proto.Message { |
| return fd.proto.GetOptions() |
| } |
| |
| // GetFileOptions returns the file's options. |
| func (fd *FileDescriptor) GetFileOptions() *dpb.FileOptions { |
| return fd.proto.GetOptions() |
| } |
| |
| // GetSourceInfo returns nil for files. It is present to satisfy the Descriptor |
| // interface. |
| func (fd *FileDescriptor) GetSourceInfo() *dpb.SourceCodeInfo_Location { |
| return nil |
| } |
| |
| // AsProto returns the underlying descriptor proto. Most usages will be more |
| // interested in AsFileDescriptorProto, which has a concrete return type. This |
| // generic version is present to satisfy the Descriptor interface. |
| func (fd *FileDescriptor) AsProto() proto.Message { |
| return fd.proto |
| } |
| |
| // AsFileDescriptorProto returns the underlying descriptor proto. |
| func (fd *FileDescriptor) AsFileDescriptorProto() *dpb.FileDescriptorProto { |
| return fd.proto |
| } |
| |
| // String returns the underlying descriptor proto, in compact text format. |
| func (fd *FileDescriptor) String() string { |
| return fd.proto.String() |
| } |
| |
| // IsProto3 returns true if the file declares a syntax of "proto3". |
| func (fd *FileDescriptor) IsProto3() bool { |
| return fd.isProto3 |
| } |
| |
| // GetDependencies returns all of this file's dependencies. These correspond to |
| // import statements in the file. |
| func (fd *FileDescriptor) GetDependencies() []*FileDescriptor { |
| return fd.deps |
| } |
| |
| // GetPublicDependencies returns all of this file's public dependencies. These |
| // correspond to public import statements in the file. |
| func (fd *FileDescriptor) GetPublicDependencies() []*FileDescriptor { |
| return fd.publicDeps |
| } |
| |
| // GetWeakDependencies returns all of this file's weak dependencies. These |
| // correspond to weak import statements in the file. |
| func (fd *FileDescriptor) GetWeakDependencies() []*FileDescriptor { |
| return fd.weakDeps |
| } |
| |
| // GetMessageTypes returns all top-level messages declared in this file. |
| func (fd *FileDescriptor) GetMessageTypes() []*MessageDescriptor { |
| return fd.messages |
| } |
| |
| // GetEnumTypes returns all top-level enums declared in this file. |
| func (fd *FileDescriptor) GetEnumTypes() []*EnumDescriptor { |
| return fd.enums |
| } |
| |
| // GetExtensions returns all top-level extensions declared in this file. |
| func (fd *FileDescriptor) GetExtensions() []*FieldDescriptor { |
| return fd.extensions |
| } |
| |
| // GetServices returns all services declared in this file. |
| func (fd *FileDescriptor) GetServices() []*ServiceDescriptor { |
| return fd.services |
| } |
| |
| // FindSymbol returns the descriptor contained within this file for the |
| // element with the given fully-qualified symbol name. If no such element |
| // exists then this method returns nil. |
| func (fd *FileDescriptor) FindSymbol(symbol string) Descriptor { |
| if symbol[0] == '.' { |
| symbol = symbol[1:] |
| } |
| return fd.symbols[symbol] |
| } |
| |
| // FindMessage finds the message with the given fully-qualified name. If no |
| // such element exists in this file then nil is returned. |
| func (fd *FileDescriptor) FindMessage(msgName string) *MessageDescriptor { |
| if md, ok := fd.symbols[msgName].(*MessageDescriptor); ok { |
| return md |
| } else { |
| return nil |
| } |
| } |
| |
| // FindEnum finds the enum with the given fully-qualified name. If no such |
| // element exists in this file then nil is returned. |
| func (fd *FileDescriptor) FindEnum(enumName string) *EnumDescriptor { |
| if ed, ok := fd.symbols[enumName].(*EnumDescriptor); ok { |
| return ed |
| } else { |
| return nil |
| } |
| } |
| |
| // FindService finds the service with the given fully-qualified name. If no |
| // such element exists in this file then nil is returned. |
| func (fd *FileDescriptor) FindService(serviceName string) *ServiceDescriptor { |
| if sd, ok := fd.symbols[serviceName].(*ServiceDescriptor); ok { |
| return sd |
| } else { |
| return nil |
| } |
| } |
| |
| // FindExtension finds the extension field for the given extended type name and |
| // tag number. If no such element exists in this file then nil is returned. |
| func (fd *FileDescriptor) FindExtension(extendeeName string, tagNumber int32) *FieldDescriptor { |
| if exd, ok := fd.fieldIndex[extendeeName][tagNumber]; ok && exd.IsExtension() { |
| return exd |
| } else { |
| return nil |
| } |
| } |
| |
| // FindExtensionByName finds the extension field with the given fully-qualified |
| // name. If no such element exists in this file then nil is returned. |
| func (fd *FileDescriptor) FindExtensionByName(extName string) *FieldDescriptor { |
| if exd, ok := fd.symbols[extName].(*FieldDescriptor); ok && exd.IsExtension() { |
| return exd |
| } else { |
| return nil |
| } |
| } |
| |
| // MessageDescriptor describes a protocol buffer message. |
| type MessageDescriptor struct { |
| proto *dpb.DescriptorProto |
| parent Descriptor |
| file *FileDescriptor |
| fields []*FieldDescriptor |
| nested []*MessageDescriptor |
| enums []*EnumDescriptor |
| extensions []*FieldDescriptor |
| oneOfs []*OneOfDescriptor |
| extRanges extRanges |
| fqn string |
| sourceInfoPath []int32 |
| jsonNames jsonNameMap |
| isProto3 bool |
| isMapEntry bool |
| } |
| |
| func createMessageDescriptor(fd *FileDescriptor, parent Descriptor, enclosing string, md *dpb.DescriptorProto, symbols map[string]Descriptor) (*MessageDescriptor, string) { |
| msgName := merge(enclosing, md.GetName()) |
| ret := &MessageDescriptor{proto: md, parent: parent, file: fd, fqn: msgName} |
| for _, f := range md.GetField() { |
| fld, n := createFieldDescriptor(fd, ret, msgName, f) |
| symbols[n] = fld |
| ret.fields = append(ret.fields, fld) |
| } |
| for _, nm := range md.NestedType { |
| nmd, n := createMessageDescriptor(fd, ret, msgName, nm, symbols) |
| symbols[n] = nmd |
| ret.nested = append(ret.nested, nmd) |
| } |
| for _, e := range md.EnumType { |
| ed, n := createEnumDescriptor(fd, ret, msgName, e, symbols) |
| symbols[n] = ed |
| ret.enums = append(ret.enums, ed) |
| } |
| for _, ex := range md.GetExtension() { |
| exd, n := createFieldDescriptor(fd, ret, msgName, ex) |
| symbols[n] = exd |
| ret.extensions = append(ret.extensions, exd) |
| } |
| for i, o := range md.GetOneofDecl() { |
| od, n := createOneOfDescriptor(fd, ret, i, msgName, o) |
| symbols[n] = od |
| ret.oneOfs = append(ret.oneOfs, od) |
| } |
| for _, r := range md.GetExtensionRange() { |
| // proto.ExtensionRange is inclusive (and that's how extension ranges are defined in code). |
| // but protoc converts range to exclusive end in descriptor, so we must convert back |
| end := r.GetEnd() - 1 |
| ret.extRanges = append(ret.extRanges, proto.ExtensionRange{ |
| Start: r.GetStart(), |
| End: end}) |
| } |
| sort.Sort(ret.extRanges) |
| ret.isProto3 = fd.isProto3 |
| ret.isMapEntry = md.GetOptions().GetMapEntry() && |
| len(ret.fields) == 2 && |
| ret.fields[0].GetNumber() == 1 && |
| ret.fields[1].GetNumber() == 2 |
| |
| return ret, msgName |
| } |
| |
| func (md *MessageDescriptor) resolve(path []int32, scopes []scope) error { |
| md.sourceInfoPath = append([]int32(nil), path...) // defensive copy |
| path = append(path, internal.Message_nestedMessagesTag) |
| scopes = append(scopes, messageScope(md)) |
| for i, nmd := range md.nested { |
| if err := nmd.resolve(append(path, int32(i)), scopes); err != nil { |
| return err |
| } |
| } |
| path[len(path)-1] = internal.Message_enumsTag |
| for i, ed := range md.enums { |
| ed.resolve(append(path, int32(i))) |
| } |
| path[len(path)-1] = internal.Message_fieldsTag |
| for i, fld := range md.fields { |
| if err := fld.resolve(append(path, int32(i)), scopes); err != nil { |
| return err |
| } |
| } |
| path[len(path)-1] = internal.Message_extensionsTag |
| for i, exd := range md.extensions { |
| if err := exd.resolve(append(path, int32(i)), scopes); err != nil { |
| return err |
| } |
| } |
| path[len(path)-1] = internal.Message_oneOfsTag |
| for i, od := range md.oneOfs { |
| od.resolve(append(path, int32(i))) |
| } |
| return nil |
| } |
| |
| // GetName returns the simple (unqualified) name of the message. |
| func (md *MessageDescriptor) GetName() string { |
| return md.proto.GetName() |
| } |
| |
| // GetFullyQualifiedName returns the fully qualified name of the message. This |
| // includes the package name (if there is one) as well as the names of any |
| // enclosing messages. |
| func (md *MessageDescriptor) GetFullyQualifiedName() string { |
| return md.fqn |
| } |
| |
| // GetParent returns the message's enclosing descriptor. For top-level messages, |
| // this will be a file descriptor. Otherwise it will be the descriptor for the |
| // enclosing message. |
| func (md *MessageDescriptor) GetParent() Descriptor { |
| return md.parent |
| } |
| |
| // GetFile returns the descriptor for the file in which this message is defined. |
| func (md *MessageDescriptor) GetFile() *FileDescriptor { |
| return md.file |
| } |
| |
| // GetOptions returns the message's options. Most usages will be more interested |
| // in GetMessageOptions, which has a concrete return type. This generic version |
| // is present to satisfy the Descriptor interface. |
| func (md *MessageDescriptor) GetOptions() proto.Message { |
| return md.proto.GetOptions() |
| } |
| |
| // GetMessageOptions returns the message's options. |
| func (md *MessageDescriptor) GetMessageOptions() *dpb.MessageOptions { |
| return md.proto.GetOptions() |
| } |
| |
| // GetSourceInfo returns source info for the message, if present in the |
| // descriptor. Not all descriptors will contain source info. If non-nil, the |
| // returned info contains information about the location in the file where the |
| // message was defined and also contains comments associated with the message |
| // definition. |
| func (md *MessageDescriptor) GetSourceInfo() *dpb.SourceCodeInfo_Location { |
| return md.file.sourceInfo.Get(md.sourceInfoPath) |
| } |
| |
| // AsProto returns the underlying descriptor proto. Most usages will be more |
| // interested in AsDescriptorProto, which has a concrete return type. This |
| // generic version is present to satisfy the Descriptor interface. |
| func (md *MessageDescriptor) AsProto() proto.Message { |
| return md.proto |
| } |
| |
| // AsDescriptorProto returns the underlying descriptor proto. |
| func (md *MessageDescriptor) AsDescriptorProto() *dpb.DescriptorProto { |
| return md.proto |
| } |
| |
| // String returns the underlying descriptor proto, in compact text format. |
| func (md *MessageDescriptor) String() string { |
| return md.proto.String() |
| } |
| |
| // IsMapEntry returns true if this is a synthetic message type that represents an entry |
| // in a map field. |
| func (md *MessageDescriptor) IsMapEntry() bool { |
| return md.isMapEntry |
| } |
| |
| // GetFields returns all of the fields for this message. |
| func (md *MessageDescriptor) GetFields() []*FieldDescriptor { |
| return md.fields |
| } |
| |
| // GetNestedMessageTypes returns all of the message types declared inside this message. |
| func (md *MessageDescriptor) GetNestedMessageTypes() []*MessageDescriptor { |
| return md.nested |
| } |
| |
| // GetNestedEnumTypes returns all of the enums declared inside this message. |
| func (md *MessageDescriptor) GetNestedEnumTypes() []*EnumDescriptor { |
| return md.enums |
| } |
| |
| // GetNestedExtensions returns all of the extensions declared inside this message. |
| func (md *MessageDescriptor) GetNestedExtensions() []*FieldDescriptor { |
| return md.extensions |
| } |
| |
| // GetOneOfs returns all of the one-of field sets declared inside this message. |
| func (md *MessageDescriptor) GetOneOfs() []*OneOfDescriptor { |
| return md.oneOfs |
| } |
| |
| // IsProto3 returns true if the file in which this message is defined declares a syntax of "proto3". |
| func (md *MessageDescriptor) IsProto3() bool { |
| return md.isProto3 |
| } |
| |
| // GetExtensionRanges returns the ranges of extension field numbers for this message. |
| func (md *MessageDescriptor) GetExtensionRanges() []proto.ExtensionRange { |
| return md.extRanges |
| } |
| |
| // IsExtendable returns true if this message has any extension ranges. |
| func (md *MessageDescriptor) IsExtendable() bool { |
| return len(md.extRanges) > 0 |
| } |
| |
| // IsExtension returns true if the given tag number is within any of this message's |
| // extension ranges. |
| func (md *MessageDescriptor) IsExtension(tagNumber int32) bool { |
| return md.extRanges.IsExtension(tagNumber) |
| } |
| |
| type extRanges []proto.ExtensionRange |
| |
| func (er extRanges) String() string { |
| var buf bytes.Buffer |
| first := true |
| for _, r := range er { |
| if first { |
| first = false |
| } else { |
| buf.WriteString(",") |
| } |
| fmt.Fprintf(&buf, "%d..%d", r.Start, r.End) |
| } |
| return buf.String() |
| } |
| |
| func (er extRanges) IsExtension(tagNumber int32) bool { |
| i := sort.Search(len(er), func(i int) bool { return er[i].End >= tagNumber }) |
| return i < len(er) && tagNumber >= er[i].Start |
| } |
| |
| func (er extRanges) Len() int { |
| return len(er) |
| } |
| |
| func (er extRanges) Less(i, j int) bool { |
| return er[i].Start < er[j].Start |
| } |
| |
| func (er extRanges) Swap(i, j int) { |
| er[i], er[j] = er[j], er[i] |
| } |
| |
| // FindFieldByName finds the field with the given name. If no such field exists |
| // then nil is returned. Only regular fields are returned, not extensions. |
| func (md *MessageDescriptor) FindFieldByName(fieldName string) *FieldDescriptor { |
| fqn := fmt.Sprintf("%s.%s", md.fqn, fieldName) |
| if fd, ok := md.file.symbols[fqn].(*FieldDescriptor); ok && !fd.IsExtension() { |
| return fd |
| } else { |
| return nil |
| } |
| } |
| |
| // FindFieldByNumber finds the field with the given tag number. If no such field |
| // exists then nil is returned. Only regular fields are returned, not extensions. |
| func (md *MessageDescriptor) FindFieldByNumber(tagNumber int32) *FieldDescriptor { |
| if fd, ok := md.file.fieldIndex[md.fqn][tagNumber]; ok && !fd.IsExtension() { |
| return fd |
| } else { |
| return nil |
| } |
| } |
| |
| // FieldDescriptor describes a field of a protocol buffer message. |
| type FieldDescriptor struct { |
| proto *dpb.FieldDescriptorProto |
| parent Descriptor |
| owner *MessageDescriptor |
| file *FileDescriptor |
| oneOf *OneOfDescriptor |
| msgType *MessageDescriptor |
| enumType *EnumDescriptor |
| fqn string |
| sourceInfoPath []int32 |
| def memoizedDefault |
| isMap bool |
| } |
| |
| func createFieldDescriptor(fd *FileDescriptor, parent Descriptor, enclosing string, fld *dpb.FieldDescriptorProto) (*FieldDescriptor, string) { |
| fldName := merge(enclosing, fld.GetName()) |
| ret := &FieldDescriptor{proto: fld, parent: parent, file: fd, fqn: fldName} |
| if fld.GetExtendee() == "" { |
| ret.owner = parent.(*MessageDescriptor) |
| } |
| // owner for extensions, field type (be it message or enum), and one-ofs get resolved later |
| return ret, fldName |
| } |
| |
| func (fd *FieldDescriptor) resolve(path []int32, scopes []scope) error { |
| if fd.proto.OneofIndex != nil && fd.oneOf == nil { |
| return fmt.Errorf("could not link field %s to one-of index %d", fd.fqn, *fd.proto.OneofIndex) |
| } |
| fd.sourceInfoPath = append([]int32(nil), path...) // defensive copy |
| if fd.proto.GetType() == dpb.FieldDescriptorProto_TYPE_ENUM { |
| if desc, err := resolve(fd.file, fd.proto.GetTypeName(), scopes); err != nil { |
| return err |
| } else { |
| fd.enumType = desc.(*EnumDescriptor) |
| } |
| } |
| if fd.proto.GetType() == dpb.FieldDescriptorProto_TYPE_MESSAGE || fd.proto.GetType() == dpb.FieldDescriptorProto_TYPE_GROUP { |
| if desc, err := resolve(fd.file, fd.proto.GetTypeName(), scopes); err != nil { |
| return err |
| } else { |
| fd.msgType = desc.(*MessageDescriptor) |
| } |
| } |
| if fd.proto.GetExtendee() != "" { |
| if desc, err := resolve(fd.file, fd.proto.GetExtendee(), scopes); err != nil { |
| return err |
| } else { |
| fd.owner = desc.(*MessageDescriptor) |
| } |
| } |
| fd.file.registerField(fd) |
| fd.isMap = fd.proto.GetLabel() == dpb.FieldDescriptorProto_LABEL_REPEATED && |
| fd.proto.GetType() == dpb.FieldDescriptorProto_TYPE_MESSAGE && |
| fd.GetMessageType().IsMapEntry() |
| return nil |
| } |
| |
| func (fd *FieldDescriptor) determineDefault() interface{} { |
| if fd.IsMap() { |
| return map[interface{}]interface{}(nil) |
| } else if fd.IsRepeated() { |
| return []interface{}(nil) |
| } else if fd.msgType != nil { |
| return nil |
| } |
| |
| proto3 := fd.file.isProto3 |
| if !proto3 { |
| def := fd.AsFieldDescriptorProto().GetDefaultValue() |
| if def != "" { |
| ret := parseDefaultValue(fd, def) |
| if ret != nil { |
| return ret |
| } |
| // if we can't parse default value, fall-through to return normal default... |
| } |
| } |
| |
| switch fd.GetType() { |
| case dpb.FieldDescriptorProto_TYPE_FIXED32, |
| dpb.FieldDescriptorProto_TYPE_UINT32: |
| return uint32(0) |
| case dpb.FieldDescriptorProto_TYPE_SFIXED32, |
| dpb.FieldDescriptorProto_TYPE_INT32, |
| dpb.FieldDescriptorProto_TYPE_SINT32: |
| return int32(0) |
| case dpb.FieldDescriptorProto_TYPE_FIXED64, |
| dpb.FieldDescriptorProto_TYPE_UINT64: |
| return uint64(0) |
| case dpb.FieldDescriptorProto_TYPE_SFIXED64, |
| dpb.FieldDescriptorProto_TYPE_INT64, |
| dpb.FieldDescriptorProto_TYPE_SINT64: |
| return int64(0) |
| case dpb.FieldDescriptorProto_TYPE_FLOAT: |
| return float32(0.0) |
| case dpb.FieldDescriptorProto_TYPE_DOUBLE: |
| return float64(0.0) |
| case dpb.FieldDescriptorProto_TYPE_BOOL: |
| return false |
| case dpb.FieldDescriptorProto_TYPE_BYTES: |
| return []byte(nil) |
| case dpb.FieldDescriptorProto_TYPE_STRING: |
| return "" |
| case dpb.FieldDescriptorProto_TYPE_ENUM: |
| if proto3 { |
| return int32(0) |
| } |
| enumVals := fd.GetEnumType().GetValues() |
| if len(enumVals) > 0 { |
| return enumVals[0].GetNumber() |
| } else { |
| return int32(0) // WTF? |
| } |
| default: |
| panic(fmt.Sprintf("Unknown field type: %v", fd.GetType())) |
| } |
| } |
| |
| func parseDefaultValue(fd *FieldDescriptor, val string) interface{} { |
| switch fd.GetType() { |
| case dpb.FieldDescriptorProto_TYPE_ENUM: |
| vd := fd.GetEnumType().FindValueByName(val) |
| if vd != nil { |
| return vd.GetNumber() |
| } |
| return nil |
| case dpb.FieldDescriptorProto_TYPE_BOOL: |
| if val == "true" { |
| return true |
| } else if val == "false" { |
| return false |
| } |
| return nil |
| case dpb.FieldDescriptorProto_TYPE_BYTES: |
| return []byte(unescape(val)) |
| case dpb.FieldDescriptorProto_TYPE_STRING: |
| return val |
| case dpb.FieldDescriptorProto_TYPE_FLOAT: |
| if f, err := strconv.ParseFloat(val, 32); err == nil { |
| return float32(f) |
| } else { |
| return float32(0) |
| } |
| case dpb.FieldDescriptorProto_TYPE_DOUBLE: |
| if f, err := strconv.ParseFloat(val, 64); err == nil { |
| return f |
| } else { |
| return float64(0) |
| } |
| case dpb.FieldDescriptorProto_TYPE_INT32, |
| dpb.FieldDescriptorProto_TYPE_SINT32, |
| dpb.FieldDescriptorProto_TYPE_SFIXED32: |
| if i, err := strconv.ParseInt(val, 10, 32); err == nil { |
| return int32(i) |
| } else { |
| return int32(0) |
| } |
| case dpb.FieldDescriptorProto_TYPE_UINT32, |
| dpb.FieldDescriptorProto_TYPE_FIXED32: |
| if i, err := strconv.ParseUint(val, 10, 32); err == nil { |
| return uint32(i) |
| } else { |
| return uint32(0) |
| } |
| case dpb.FieldDescriptorProto_TYPE_INT64, |
| dpb.FieldDescriptorProto_TYPE_SINT64, |
| dpb.FieldDescriptorProto_TYPE_SFIXED64: |
| if i, err := strconv.ParseInt(val, 10, 64); err == nil { |
| return i |
| } else { |
| return int64(0) |
| } |
| case dpb.FieldDescriptorProto_TYPE_UINT64, |
| dpb.FieldDescriptorProto_TYPE_FIXED64: |
| if i, err := strconv.ParseUint(val, 10, 64); err == nil { |
| return i |
| } else { |
| return uint64(0) |
| } |
| default: |
| return nil |
| } |
| } |
| |
| func unescape(s string) string { |
| // protoc encodes default values for 'bytes' fields using C escaping, |
| // so this function reverses that escaping |
| out := make([]byte, 0, len(s)) |
| var buf [4]byte |
| for len(s) > 0 { |
| if s[0] != '\\' || len(s) < 2 { |
| // not escape sequence, or too short to be well-formed escape |
| out = append(out, s[0]) |
| s = s[1:] |
| } else if s[1] == 'x' || s[1] == 'X' { |
| n := matchPrefix(s[2:], 2, isHex) |
| if n == 0 { |
| // bad escape |
| out = append(out, s[:2]...) |
| s = s[2:] |
| } else { |
| c, err := strconv.ParseUint(s[2:2+n], 16, 8) |
| if err != nil { |
| // shouldn't really happen... |
| out = append(out, s[:2+n]...) |
| } else { |
| out = append(out, byte(c)) |
| } |
| s = s[2+n:] |
| } |
| } else if s[1] >= '0' && s[1] <= '7' { |
| n := 1 + matchPrefix(s[2:], 2, isOctal) |
| c, err := strconv.ParseUint(s[1:1+n], 8, 8) |
| if err != nil || c > 0xff { |
| out = append(out, s[:1+n]...) |
| } else { |
| out = append(out, byte(c)) |
| } |
| s = s[1+n:] |
| } else if s[1] == 'u' { |
| if len(s) < 6 { |
| // bad escape |
| out = append(out, s...) |
| s = s[len(s):] |
| } else { |
| c, err := strconv.ParseUint(s[2:6], 16, 16) |
| if err != nil { |
| // bad escape |
| out = append(out, s[:6]...) |
| } else { |
| w := utf8.EncodeRune(buf[:], rune(c)) |
| out = append(out, buf[:w]...) |
| } |
| s = s[6:] |
| } |
| } else if s[1] == 'U' { |
| if len(s) < 10 { |
| // bad escape |
| out = append(out, s...) |
| s = s[len(s):] |
| } else { |
| c, err := strconv.ParseUint(s[2:10], 16, 32) |
| if err != nil || c > 0x10ffff { |
| // bad escape |
| out = append(out, s[:10]...) |
| } else { |
| w := utf8.EncodeRune(buf[:], rune(c)) |
| out = append(out, buf[:w]...) |
| } |
| s = s[10:] |
| } |
| } else { |
| switch s[1] { |
| case 'a': |
| out = append(out, '\a') |
| case 'b': |
| out = append(out, '\b') |
| case 'f': |
| out = append(out, '\f') |
| case 'n': |
| out = append(out, '\n') |
| case 'r': |
| out = append(out, '\r') |
| case 't': |
| out = append(out, '\t') |
| case 'v': |
| out = append(out, '\v') |
| case '\\': |
| out = append(out, '\\') |
| case '\'': |
| out = append(out, '\'') |
| case '"': |
| out = append(out, '"') |
| case '?': |
| out = append(out, '?') |
| default: |
| // invalid escape, just copy it as-is |
| out = append(out, s[:2]...) |
| } |
| s = s[2:] |
| } |
| } |
| return string(out) |
| } |
| |
| func isOctal(b byte) bool { return b >= '0' && b <= '7' } |
| func isHex(b byte) bool { |
| return (b >= '0' && b <= '9') || (b >= 'a' && b <= 'f') || (b >= 'A' && b <= 'F') |
| } |
| func matchPrefix(s string, limit int, fn func(byte) bool) int { |
| l := len(s) |
| if l > limit { |
| l = limit |
| } |
| i := 0 |
| for ; i < l; i++ { |
| if !fn(s[i]) { |
| return i |
| } |
| } |
| return i |
| } |
| |
| // GetName returns the name of the field. |
| func (fd *FieldDescriptor) GetName() string { |
| return fd.proto.GetName() |
| } |
| |
| // GetNumber returns the tag number of this field. |
| func (fd *FieldDescriptor) GetNumber() int32 { |
| return fd.proto.GetNumber() |
| } |
| |
| // GetFullyQualifiedName returns the fully qualified name of the field. Unlike |
| // GetName, this includes fully qualified name of the enclosing message for |
| // regular fields. |
| // |
| // For extension fields, this includes the package (if there is one) as well as |
| // any enclosing messages. The package and/or enclosing messages are for where |
| // the extension is defined, not the message it extends. |
| // |
| // If this field is part of a one-of, the fully qualified name does *not* |
| // include the name of the one-of, only of the enclosing message. |
| func (fd *FieldDescriptor) GetFullyQualifiedName() string { |
| return fd.fqn |
| } |
| |
| // GetParent returns the fields's enclosing descriptor. For normal |
| // (non-extension) fields, this is the enclosing message. For extensions, this |
| // is the descriptor in which the extension is defined, not the message that is |
| // extended. The parent for an extension may be a file descriptor or a message, |
| // depending on where the extension is defined. |
| func (fd *FieldDescriptor) GetParent() Descriptor { |
| return fd.parent |
| } |
| |
| // GetFile returns the descriptor for the file in which this field is defined. |
| func (fd *FieldDescriptor) GetFile() *FileDescriptor { |
| return fd.file |
| } |
| |
| // GetOptions returns the field's options. Most usages will be more interested |
| // in GetFieldOptions, which has a concrete return type. This generic version |
| // is present to satisfy the Descriptor interface. |
| func (fd *FieldDescriptor) GetOptions() proto.Message { |
| return fd.proto.GetOptions() |
| } |
| |
| // GetFieldOptions returns the field's options. |
| func (fd *FieldDescriptor) GetFieldOptions() *dpb.FieldOptions { |
| return fd.proto.GetOptions() |
| } |
| |
| // GetSourceInfo returns source info for the field, if present in the |
| // descriptor. Not all descriptors will contain source info. If non-nil, the |
| // returned info contains information about the location in the file where the |
| // field was defined and also contains comments associated with the field |
| // definition. |
| func (fd *FieldDescriptor) GetSourceInfo() *dpb.SourceCodeInfo_Location { |
| return fd.file.sourceInfo.Get(fd.sourceInfoPath) |
| } |
| |
| // AsProto returns the underlying descriptor proto. Most usages will be more |
| // interested in AsFieldDescriptorProto, which has a concrete return type. This |
| // generic version is present to satisfy the Descriptor interface. |
| func (fd *FieldDescriptor) AsProto() proto.Message { |
| return fd.proto |
| } |
| |
| // AsFieldDescriptorProto returns the underlying descriptor proto. |
| func (fd *FieldDescriptor) AsFieldDescriptorProto() *dpb.FieldDescriptorProto { |
| return fd.proto |
| } |
| |
| // String returns the underlying descriptor proto, in compact text format. |
| func (fd *FieldDescriptor) String() string { |
| return fd.proto.String() |
| } |
| |
| // GetJSONName returns the name of the field as referenced in the message's JSON |
| // format. |
| func (fd *FieldDescriptor) GetJSONName() string { |
| if jsonName := fd.proto.GetJsonName(); jsonName != "" { |
| return jsonName |
| } |
| return fd.proto.GetName() |
| } |
| |
| // GetFullyQualifiedJSONName returns the JSON format name (same as GetJSONName), |
| // but includes the fully qualified name of the enclosing message. |
| // |
| // If the field is an extension, it will return the package name (if there is |
| // one) as well as the names of any enclosing messages. The package and/or |
| // enclosing messages are for where the extension is defined, not the message it |
| // extends. |
| func (fd *FieldDescriptor) GetFullyQualifiedJSONName() string { |
| parent := fd.GetParent() |
| switch parent := parent.(type) { |
| case *FileDescriptor: |
| pkg := parent.GetPackage() |
| if pkg == "" { |
| return fd.GetJSONName() |
| } |
| return fmt.Sprintf("%s.%s", pkg, fd.GetJSONName()) |
| default: |
| return fmt.Sprintf("%s.%s", parent.GetFullyQualifiedName(), fd.GetJSONName()) |
| } |
| } |
| |
| // GetOwner returns the message type that this field belongs to. If this is a normal |
| // field then this is the same as GetParent. But for extensions, this will be the |
| // extendee message whereas GetParent refers to where the extension was declared. |
| func (fd *FieldDescriptor) GetOwner() *MessageDescriptor { |
| return fd.owner |
| } |
| |
| // IsExtension returns true if this is an extension field. |
| func (fd *FieldDescriptor) IsExtension() bool { |
| return fd.proto.GetExtendee() != "" |
| } |
| |
| // GetOneOf returns the one-of field set to which this field belongs. If this field |
| // is not part of a one-of then this method returns nil. |
| func (fd *FieldDescriptor) GetOneOf() *OneOfDescriptor { |
| return fd.oneOf |
| } |
| |
| // GetType returns the type of this field. If the type indicates an enum, the |
| // enum type can be queried via GetEnumType. If the type indicates a message, the |
| // message type can be queried via GetMessageType. |
| func (fd *FieldDescriptor) GetType() dpb.FieldDescriptorProto_Type { |
| return fd.proto.GetType() |
| } |
| |
| // GetLabel returns the label for this field. The label can be required (proto2-only), |
| // optional (default for proto3), or required. |
| func (fd *FieldDescriptor) GetLabel() dpb.FieldDescriptorProto_Label { |
| return fd.proto.GetLabel() |
| } |
| |
| // IsRequired returns true if this field has the "required" label. |
| func (fd *FieldDescriptor) IsRequired() bool { |
| return fd.proto.GetLabel() == dpb.FieldDescriptorProto_LABEL_REQUIRED |
| } |
| |
| // IsRepeated returns true if this field has the "repeated" label. |
| func (fd *FieldDescriptor) IsRepeated() bool { |
| return fd.proto.GetLabel() == dpb.FieldDescriptorProto_LABEL_REPEATED |
| } |
| |
| // IsMap returns true if this is a map field. If so, it will have the "repeated" |
| // label its type will be a message that represents a map entry. The map entry |
| // message will have exactly two fields: tag #1 is the key and tag #2 is the value. |
| func (fd *FieldDescriptor) IsMap() bool { |
| return fd.isMap |
| } |
| |
| // GetMapKeyType returns the type of the key field if this is a map field. If it is |
| // not a map field, nil is returned. |
| func (fd *FieldDescriptor) GetMapKeyType() *FieldDescriptor { |
| if fd.isMap { |
| return fd.msgType.FindFieldByNumber(int32(1)) |
| } |
| return nil |
| } |
| |
| // GetMapValueType returns the type of the value field if this is a map field. If it |
| // is not a map field, nil is returned. |
| func (fd *FieldDescriptor) GetMapValueType() *FieldDescriptor { |
| if fd.isMap { |
| return fd.msgType.FindFieldByNumber(int32(2)) |
| } |
| return nil |
| } |
| |
| // GetMessageType returns the type of this field if it is a message type. If |
| // this field is not a message type, it returns nil. |
| func (fd *FieldDescriptor) GetMessageType() *MessageDescriptor { |
| return fd.msgType |
| } |
| |
| // GetEnumType returns the type of this field if it is an enum type. If this |
| // field is not an enum type, it returns nil. |
| func (fd *FieldDescriptor) GetEnumType() *EnumDescriptor { |
| return fd.enumType |
| } |
| |
| // GetDefaultValue returns the default value for this field. |
| // |
| // If this field represents a message type, this method always returns nil (even though |
| // for proto2 files, the default value should be a default instance of the message type). |
| // If the field represents an enum type, this method returns an int32 corresponding to the |
| // enum value. If this field is a map, it returns a nil map[interface{}]interface{}. If |
| // this field is repeated (and not a map), it returns a nil []interface{}. |
| // |
| // Otherwise, it returns the declared default value for the field or a zero value, if no |
| // default is declared or if the file is proto3. The type of said return value corresponds |
| // to the type of the field: |
| // +-------------------------+-----------+ |
| // | Declared Type | Go Type | |
| // +-------------------------+-----------+ |
| // | int32, sint32, sfixed32 | int32 | |
| // | int64, sint64, sfixed64 | int64 | |
| // | uint32, fixed32 | uint32 | |
| // | uint64, fixed64 | uint64 | |
| // | float | float32 | |
| // | double | double32 | |
| // | bool | bool | |
| // | string | string | |
| // | bytes | []byte | |
| // +-------------------------+-----------+ |
| func (fd *FieldDescriptor) GetDefaultValue() interface{} { |
| return fd.getDefaultValue() |
| } |
| |
| // EnumDescriptor describes an enum declared in a proto file. |
| type EnumDescriptor struct { |
| proto *dpb.EnumDescriptorProto |
| parent Descriptor |
| file *FileDescriptor |
| values []*EnumValueDescriptor |
| valuesByNum sortedValues |
| fqn string |
| sourceInfoPath []int32 |
| } |
| |
| func createEnumDescriptor(fd *FileDescriptor, parent Descriptor, enclosing string, ed *dpb.EnumDescriptorProto, symbols map[string]Descriptor) (*EnumDescriptor, string) { |
| enumName := merge(enclosing, ed.GetName()) |
| ret := &EnumDescriptor{proto: ed, parent: parent, file: fd, fqn: enumName} |
| for _, ev := range ed.GetValue() { |
| evd, n := createEnumValueDescriptor(fd, ret, enumName, ev) |
| symbols[n] = evd |
| ret.values = append(ret.values, evd) |
| } |
| if len(ret.values) > 0 { |
| ret.valuesByNum = make(sortedValues, len(ret.values)) |
| copy(ret.valuesByNum, ret.values) |
| sort.Stable(ret.valuesByNum) |
| } |
| return ret, enumName |
| } |
| |
| type sortedValues []*EnumValueDescriptor |
| |
| func (sv sortedValues) Len() int { |
| return len(sv) |
| } |
| |
| func (sv sortedValues) Less(i, j int) bool { |
| return sv[i].GetNumber() < sv[j].GetNumber() |
| } |
| |
| func (sv sortedValues) Swap(i, j int) { |
| sv[i], sv[j] = sv[j], sv[i] |
| } |
| |
| func (ed *EnumDescriptor) resolve(path []int32) { |
| ed.sourceInfoPath = append([]int32(nil), path...) // defensive copy |
| path = append(path, internal.Enum_valuesTag) |
| for i, evd := range ed.values { |
| evd.resolve(append(path, int32(i))) |
| } |
| } |
| |
| // GetName returns the simple (unqualified) name of the enum type. |
| func (ed *EnumDescriptor) GetName() string { |
| return ed.proto.GetName() |
| } |
| |
| // GetFullyQualifiedName returns the fully qualified name of the enum type. |
| // This includes the package name (if there is one) as well as the names of any |
| // enclosing messages. |
| func (ed *EnumDescriptor) GetFullyQualifiedName() string { |
| return ed.fqn |
| } |
| |
| // GetParent returns the enum type's enclosing descriptor. For top-level enums, |
| // this will be a file descriptor. Otherwise it will be the descriptor for the |
| // enclosing message. |
| func (ed *EnumDescriptor) GetParent() Descriptor { |
| return ed.parent |
| } |
| |
| // GetFile returns the descriptor for the file in which this enum is defined. |
| func (ed *EnumDescriptor) GetFile() *FileDescriptor { |
| return ed.file |
| } |
| |
| // GetOptions returns the enum type's options. Most usages will be more |
| // interested in GetEnumOptions, which has a concrete return type. This generic |
| // version is present to satisfy the Descriptor interface. |
| func (ed *EnumDescriptor) GetOptions() proto.Message { |
| return ed.proto.GetOptions() |
| } |
| |
| // GetEnumOptions returns the enum type's options. |
| func (ed *EnumDescriptor) GetEnumOptions() *dpb.EnumOptions { |
| return ed.proto.GetOptions() |
| } |
| |
| // GetSourceInfo returns source info for the enum type, if present in the |
| // descriptor. Not all descriptors will contain source info. If non-nil, the |
| // returned info contains information about the location in the file where the |
| // enum type was defined and also contains comments associated with the enum |
| // definition. |
| func (ed *EnumDescriptor) GetSourceInfo() *dpb.SourceCodeInfo_Location { |
| return ed.file.sourceInfo.Get(ed.sourceInfoPath) |
| } |
| |
| // AsProto returns the underlying descriptor proto. Most usages will be more |
| // interested in AsEnumDescriptorProto, which has a concrete return type. This |
| // generic version is present to satisfy the Descriptor interface. |
| func (ed *EnumDescriptor) AsProto() proto.Message { |
| return ed.proto |
| } |
| |
| // AsEnumDescriptorProto returns the underlying descriptor proto. |
| func (ed *EnumDescriptor) AsEnumDescriptorProto() *dpb.EnumDescriptorProto { |
| return ed.proto |
| } |
| |
| // String returns the underlying descriptor proto, in compact text format. |
| func (ed *EnumDescriptor) String() string { |
| return ed.proto.String() |
| } |
| |
| // GetValues returns all of the allowed values defined for this enum. |
| func (ed *EnumDescriptor) GetValues() []*EnumValueDescriptor { |
| return ed.values |
| } |
| |
| // FindValueByName finds the enum value with the given name. If no such value exists |
| // then nil is returned. |
| func (ed *EnumDescriptor) FindValueByName(name string) *EnumValueDescriptor { |
| fqn := fmt.Sprintf("%s.%s", ed.fqn, name) |
| if vd, ok := ed.file.symbols[fqn].(*EnumValueDescriptor); ok { |
| return vd |
| } else { |
| return nil |
| } |
| } |
| |
| // FindValueByNumber finds the value with the given numeric value. If no such value |
| // exists then nil is returned. If aliases are allowed and multiple values have the |
| // given number, the first declared value is returned. |
| func (ed *EnumDescriptor) FindValueByNumber(num int32) *EnumValueDescriptor { |
| index := sort.Search(len(ed.valuesByNum), func(i int) bool { return ed.valuesByNum[i].GetNumber() >= num }) |
| if index < len(ed.valuesByNum) { |
| vd := ed.valuesByNum[index] |
| if vd.GetNumber() == num { |
| return vd |
| } |
| } |
| return nil |
| } |
| |
| // EnumValueDescriptor describes an allowed value of an enum declared in a proto file. |
| type EnumValueDescriptor struct { |
| proto *dpb.EnumValueDescriptorProto |
| parent *EnumDescriptor |
| file *FileDescriptor |
| fqn string |
| sourceInfoPath []int32 |
| } |
| |
| func createEnumValueDescriptor(fd *FileDescriptor, parent *EnumDescriptor, enclosing string, evd *dpb.EnumValueDescriptorProto) (*EnumValueDescriptor, string) { |
| valName := merge(enclosing, evd.GetName()) |
| return &EnumValueDescriptor{proto: evd, parent: parent, file: fd, fqn: valName}, valName |
| } |
| |
| func (vd *EnumValueDescriptor) resolve(path []int32) { |
| vd.sourceInfoPath = append([]int32(nil), path...) // defensive copy |
| } |
| |
| // GetName returns the name of the enum value. |
| func (vd *EnumValueDescriptor) GetName() string { |
| return vd.proto.GetName() |
| } |
| |
| // GetNumber returns the numeric value associated with this enum value. |
| func (vd *EnumValueDescriptor) GetNumber() int32 { |
| return vd.proto.GetNumber() |
| } |
| |
| // GetFullyQualifiedName returns the fully qualified name of the enum value. |
| // Unlike GetName, this includes fully qualified name of the enclosing enum. |
| func (vd *EnumValueDescriptor) GetFullyQualifiedName() string { |
| return vd.fqn |
| } |
| |
| // GetParent returns the descriptor for the enum in which this enum value is |
| // defined. Most usages will prefer to use GetEnum, which has a concrete return |
| // type. This more generic method is present to satisfy the Descriptor interface. |
| func (vd *EnumValueDescriptor) GetParent() Descriptor { |
| return vd.parent |
| } |
| |
| // GetEnum returns the enum in which this enum value is defined. |
| func (vd *EnumValueDescriptor) GetEnum() *EnumDescriptor { |
| return vd.parent |
| } |
| |
| // GetFile returns the descriptor for the file in which this enum value is |
| // defined. |
| func (vd *EnumValueDescriptor) GetFile() *FileDescriptor { |
| return vd.file |
| } |
| |
| // GetOptions returns the enum value's options. Most usages will be more |
| // interested in GetEnumValueOptions, which has a concrete return type. This |
| // generic version is present to satisfy the Descriptor interface. |
| func (vd *EnumValueDescriptor) GetOptions() proto.Message { |
| return vd.proto.GetOptions() |
| } |
| |
| // GetEnumValueOptions returns the enum value's options. |
| func (vd *EnumValueDescriptor) GetEnumValueOptions() *dpb.EnumValueOptions { |
| return vd.proto.GetOptions() |
| } |
| |
| // GetSourceInfo returns source info for the enum value, if present in the |
| // descriptor. Not all descriptors will contain source info. If non-nil, the |
| // returned info contains information about the location in the file where the |
| // enum value was defined and also contains comments associated with the enum |
| // value definition. |
| func (vd *EnumValueDescriptor) GetSourceInfo() *dpb.SourceCodeInfo_Location { |
| return vd.file.sourceInfo.Get(vd.sourceInfoPath) |
| } |
| |
| // AsProto returns the underlying descriptor proto. Most usages will be more |
| // interested in AsEnumValueDescriptorProto, which has a concrete return type. |
| // This generic version is present to satisfy the Descriptor interface. |
| func (vd *EnumValueDescriptor) AsProto() proto.Message { |
| return vd.proto |
| } |
| |
| // AsEnumValueDescriptorProto returns the underlying descriptor proto. |
| func (vd *EnumValueDescriptor) AsEnumValueDescriptorProto() *dpb.EnumValueDescriptorProto { |
| return vd.proto |
| } |
| |
| // String returns the underlying descriptor proto, in compact text format. |
| func (vd *EnumValueDescriptor) String() string { |
| return vd.proto.String() |
| } |
| |
| // ServiceDescriptor describes an RPC service declared in a proto file. |
| type ServiceDescriptor struct { |
| proto *dpb.ServiceDescriptorProto |
| file *FileDescriptor |
| methods []*MethodDescriptor |
| fqn string |
| sourceInfoPath []int32 |
| } |
| |
| func createServiceDescriptor(fd *FileDescriptor, enclosing string, sd *dpb.ServiceDescriptorProto, symbols map[string]Descriptor) (*ServiceDescriptor, string) { |
| serviceName := merge(enclosing, sd.GetName()) |
| ret := &ServiceDescriptor{proto: sd, file: fd, fqn: serviceName} |
| for _, m := range sd.GetMethod() { |
| md, n := createMethodDescriptor(fd, ret, serviceName, m) |
| symbols[n] = md |
| ret.methods = append(ret.methods, md) |
| } |
| return ret, serviceName |
| } |
| |
| func (sd *ServiceDescriptor) resolve(path []int32, scopes []scope) error { |
| sd.sourceInfoPath = append([]int32(nil), path...) // defensive copy |
| path = append(path, internal.Service_methodsTag) |
| for i, md := range sd.methods { |
| if err := md.resolve(append(path, int32(i)), scopes); err != nil { |
| return err |
| } |
| } |
| return nil |
| } |
| |
| // GetName returns the simple (unqualified) name of the service. |
| func (sd *ServiceDescriptor) GetName() string { |
| return sd.proto.GetName() |
| } |
| |
| // GetFullyQualifiedName returns the fully qualified name of the service. This |
| // includes the package name (if there is one). |
| func (sd *ServiceDescriptor) GetFullyQualifiedName() string { |
| return sd.fqn |
| } |
| |
| // GetParent returns the descriptor for the file in which this service is |
| // defined. Most usages will prefer to use GetFile, which has a concrete return |
| // type. This more generic method is present to satisfy the Descriptor interface. |
| func (sd *ServiceDescriptor) GetParent() Descriptor { |
| return sd.file |
| } |
| |
| // GetFile returns the descriptor for the file in which this service is defined. |
| func (sd *ServiceDescriptor) GetFile() *FileDescriptor { |
| return sd.file |
| } |
| |
| // GetOptions returns the service's options. Most usages will be more interested |
| // in GetServiceOptions, which has a concrete return type. This generic version |
| // is present to satisfy the Descriptor interface. |
| func (sd *ServiceDescriptor) GetOptions() proto.Message { |
| return sd.proto.GetOptions() |
| } |
| |
| // GetServiceOptions returns the service's options. |
| func (sd *ServiceDescriptor) GetServiceOptions() *dpb.ServiceOptions { |
| return sd.proto.GetOptions() |
| } |
| |
| // GetSourceInfo returns source info for the service, if present in the |
| // descriptor. Not all descriptors will contain source info. If non-nil, the |
| // returned info contains information about the location in the file where the |
| // service was defined and also contains comments associated with the service |
| // definition. |
| func (sd *ServiceDescriptor) GetSourceInfo() *dpb.SourceCodeInfo_Location { |
| return sd.file.sourceInfo.Get(sd.sourceInfoPath) |
| } |
| |
| // AsProto returns the underlying descriptor proto. Most usages will be more |
| // interested in AsServiceDescriptorProto, which has a concrete return type. |
| // This generic version is present to satisfy the Descriptor interface. |
| func (sd *ServiceDescriptor) AsProto() proto.Message { |
| return sd.proto |
| } |
| |
| // AsServiceDescriptorProto returns the underlying descriptor proto. |
| func (sd *ServiceDescriptor) AsServiceDescriptorProto() *dpb.ServiceDescriptorProto { |
| return sd.proto |
| } |
| |
| // String returns the underlying descriptor proto, in compact text format. |
| func (sd *ServiceDescriptor) String() string { |
| return sd.proto.String() |
| } |
| |
| // GetMethods returns all of the RPC methods for this service. |
| func (sd *ServiceDescriptor) GetMethods() []*MethodDescriptor { |
| return sd.methods |
| } |
| |
| // FindMethodByName finds the method with the given name. If no such method exists |
| // then nil is returned. |
| func (sd *ServiceDescriptor) FindMethodByName(name string) *MethodDescriptor { |
| fqn := fmt.Sprintf("%s.%s", sd.fqn, name) |
| if md, ok := sd.file.symbols[fqn].(*MethodDescriptor); ok { |
| return md |
| } else { |
| return nil |
| } |
| } |
| |
| // MethodDescriptor describes an RPC method declared in a proto file. |
| type MethodDescriptor struct { |
| proto *dpb.MethodDescriptorProto |
| parent *ServiceDescriptor |
| file *FileDescriptor |
| inType *MessageDescriptor |
| outType *MessageDescriptor |
| fqn string |
| sourceInfoPath []int32 |
| } |
| |
| func createMethodDescriptor(fd *FileDescriptor, parent *ServiceDescriptor, enclosing string, md *dpb.MethodDescriptorProto) (*MethodDescriptor, string) { |
| // request and response types get resolved later |
| methodName := merge(enclosing, md.GetName()) |
| return &MethodDescriptor{proto: md, parent: parent, file: fd, fqn: methodName}, methodName |
| } |
| |
| func (md *MethodDescriptor) resolve(path []int32, scopes []scope) error { |
| md.sourceInfoPath = append([]int32(nil), path...) // defensive copy |
| if desc, err := resolve(md.file, md.proto.GetInputType(), scopes); err != nil { |
| return err |
| } else { |
| md.inType = desc.(*MessageDescriptor) |
| } |
| if desc, err := resolve(md.file, md.proto.GetOutputType(), scopes); err != nil { |
| return err |
| } else { |
| md.outType = desc.(*MessageDescriptor) |
| } |
| return nil |
| } |
| |
| // GetName returns the name of the method. |
| func (md *MethodDescriptor) GetName() string { |
| return md.proto.GetName() |
| } |
| |
| // GetFullyQualifiedName returns the fully qualified name of the method. Unlike |
| // GetName, this includes fully qualified name of the enclosing service. |
| func (md *MethodDescriptor) GetFullyQualifiedName() string { |
| return md.fqn |
| } |
| |
| // GetParent returns the descriptor for the service in which this method is |
| // defined. Most usages will prefer to use GetService, which has a concrete |
| // return type. This more generic method is present to satisfy the Descriptor |
| // interface. |
| func (md *MethodDescriptor) GetParent() Descriptor { |
| return md.parent |
| } |
| |
| // GetService returns the RPC service in which this method is declared. |
| func (md *MethodDescriptor) GetService() *ServiceDescriptor { |
| return md.parent |
| } |
| |
| // GetFile returns the descriptor for the file in which this method is defined. |
| func (md *MethodDescriptor) GetFile() *FileDescriptor { |
| return md.file |
| } |
| |
| // GetOptions returns the method's options. Most usages will be more interested |
| // in GetMethodOptions, which has a concrete return type. This generic version |
| // is present to satisfy the Descriptor interface. |
| func (md *MethodDescriptor) GetOptions() proto.Message { |
| return md.proto.GetOptions() |
| } |
| |
| // GetMethodOptions returns the method's options. |
| func (md *MethodDescriptor) GetMethodOptions() *dpb.MethodOptions { |
| return md.proto.GetOptions() |
| } |
| |
| // GetSourceInfo returns source info for the method, if present in the |
| // descriptor. Not all descriptors will contain source info. If non-nil, the |
| // returned info contains information about the location in the file where the |
| // method was defined and also contains comments associated with the method |
| // definition. |
| func (md *MethodDescriptor) GetSourceInfo() *dpb.SourceCodeInfo_Location { |
| return md.file.sourceInfo.Get(md.sourceInfoPath) |
| } |
| |
| // AsProto returns the underlying descriptor proto. Most usages will be more |
| // interested in AsMethodDescriptorProto, which has a concrete return type. This |
| // generic version is present to satisfy the Descriptor interface. |
| func (md *MethodDescriptor) AsProto() proto.Message { |
| return md.proto |
| } |
| |
| // AsMethodDescriptorProto returns the underlying descriptor proto. |
| func (md *MethodDescriptor) AsMethodDescriptorProto() *dpb.MethodDescriptorProto { |
| return md.proto |
| } |
| |
| // String returns the underlying descriptor proto, in compact text format. |
| func (md *MethodDescriptor) String() string { |
| return md.proto.String() |
| } |
| |
| // IsServerStreaming returns true if this is a server-streaming method. |
| func (md *MethodDescriptor) IsServerStreaming() bool { |
| return md.proto.GetServerStreaming() |
| } |
| |
| // IsClientStreaming returns true if this is a client-streaming method. |
| func (md *MethodDescriptor) IsClientStreaming() bool { |
| return md.proto.GetClientStreaming() |
| } |
| |
| // GetInputType returns the input type, or request type, of the RPC method. |
| func (md *MethodDescriptor) GetInputType() *MessageDescriptor { |
| return md.inType |
| } |
| |
| // GetOutputType returns the output type, or response type, of the RPC method. |
| func (md *MethodDescriptor) GetOutputType() *MessageDescriptor { |
| return md.outType |
| } |
| |
| // OneOfDescriptor describes a one-of field set declared in a protocol buffer message. |
| type OneOfDescriptor struct { |
| proto *dpb.OneofDescriptorProto |
| parent *MessageDescriptor |
| file *FileDescriptor |
| choices []*FieldDescriptor |
| fqn string |
| sourceInfoPath []int32 |
| } |
| |
| func createOneOfDescriptor(fd *FileDescriptor, parent *MessageDescriptor, index int, enclosing string, od *dpb.OneofDescriptorProto) (*OneOfDescriptor, string) { |
| oneOfName := merge(enclosing, od.GetName()) |
| ret := &OneOfDescriptor{proto: od, parent: parent, file: fd, fqn: oneOfName} |
| for _, f := range parent.fields { |
| oi := f.proto.OneofIndex |
| if oi != nil && *oi == int32(index) { |
| f.oneOf = ret |
| ret.choices = append(ret.choices, f) |
| } |
| } |
| return ret, oneOfName |
| } |
| |
| func (od *OneOfDescriptor) resolve(path []int32) { |
| od.sourceInfoPath = append([]int32(nil), path...) // defensive copy |
| } |
| |
| // GetName returns the name of the one-of. |
| func (od *OneOfDescriptor) GetName() string { |
| return od.proto.GetName() |
| } |
| |
| // GetFullyQualifiedName returns the fully qualified name of the one-of. Unlike |
| // GetName, this includes fully qualified name of the enclosing message. |
| func (od *OneOfDescriptor) GetFullyQualifiedName() string { |
| return od.fqn |
| } |
| |
| // GetParent returns the descriptor for the message in which this one-of is |
| // defined. Most usages will prefer to use GetOwner, which has a concrete |
| // return type. This more generic method is present to satisfy the Descriptor |
| // interface. |
| func (od *OneOfDescriptor) GetParent() Descriptor { |
| return od.parent |
| } |
| |
| // GetOwner returns the message to which this one-of field set belongs. |
| func (od *OneOfDescriptor) GetOwner() *MessageDescriptor { |
| return od.parent |
| } |
| |
| // GetFile returns the descriptor for the file in which this one-fof is defined. |
| func (od *OneOfDescriptor) GetFile() *FileDescriptor { |
| return od.file |
| } |
| |
| // GetOptions returns the one-of's options. Most usages will be more interested |
| // in GetOneOfOptions, which has a concrete return type. This generic version |
| // is present to satisfy the Descriptor interface. |
| func (od *OneOfDescriptor) GetOptions() proto.Message { |
| return od.proto.GetOptions() |
| } |
| |
| // GetOneOfOptions returns the one-of's options. |
| func (od *OneOfDescriptor) GetOneOfOptions() *dpb.OneofOptions { |
| return od.proto.GetOptions() |
| } |
| |
| // GetSourceInfo returns source info for the one-of, if present in the |
| // descriptor. Not all descriptors will contain source info. If non-nil, the |
| // returned info contains information about the location in the file where the |
| // one-of was defined and also contains comments associated with the one-of |
| // definition. |
| func (od *OneOfDescriptor) GetSourceInfo() *dpb.SourceCodeInfo_Location { |
| return od.file.sourceInfo.Get(od.sourceInfoPath) |
| } |
| |
| // AsProto returns the underlying descriptor proto. Most usages will be more |
| // interested in AsOneofDescriptorProto, which has a concrete return type. This |
| // generic version is present to satisfy the Descriptor interface. |
| func (od *OneOfDescriptor) AsProto() proto.Message { |
| return od.proto |
| } |
| |
| // AsOneofDescriptorProto returns the underlying descriptor proto. |
| func (od *OneOfDescriptor) AsOneofDescriptorProto() *dpb.OneofDescriptorProto { |
| return od.proto |
| } |
| |
| // String returns the underlying descriptor proto, in compact text format. |
| func (od *OneOfDescriptor) String() string { |
| return od.proto.String() |
| } |
| |
| // GetChoices returns the fields that are part of the one-of field set. At most one of |
| // these fields may be set for a given message. |
| func (od *OneOfDescriptor) GetChoices() []*FieldDescriptor { |
| return od.choices |
| } |
| |
| // scope represents a lexical scope in a proto file in which messages and enums |
| // can be declared. |
| type scope func(string) Descriptor |
| |
| func fileScope(fd *FileDescriptor) scope { |
| // we search symbols in this file, but also symbols in other files that have |
| // the same package as this file or a "parent" package (in protobuf, |
| // packages are a hierarchy like C++ namespaces) |
| prefixes := internal.CreatePrefixList(fd.proto.GetPackage()) |
| return func(name string) Descriptor { |
| for _, prefix := range prefixes { |
| n := merge(prefix, name) |
| d := findSymbol(fd, n, false) |
| if d != nil { |
| return d |
| } |
| } |
| return nil |
| } |
| } |
| |
| func messageScope(md *MessageDescriptor) scope { |
| return func(name string) Descriptor { |
| n := merge(md.fqn, name) |
| if d, ok := md.file.symbols[n]; ok { |
| return d |
| } |
| return nil |
| } |
| } |
| |
| func resolve(fd *FileDescriptor, name string, scopes []scope) (Descriptor, error) { |
| if strings.HasPrefix(name, ".") { |
| // already fully-qualified |
| d := findSymbol(fd, name[1:], false) |
| if d != nil { |
| return d, nil |
| } |
| } else { |
| // unqualified, so we look in the enclosing (last) scope first and move |
| // towards outermost (first) scope, trying to resolve the symbol |
| for i := len(scopes) - 1; i >= 0; i-- { |
| d := scopes[i](name) |
| if d != nil { |
| return d, nil |
| } |
| } |
| } |
| return nil, fmt.Errorf("file %q included an unresolvable reference to %q", fd.proto.GetName(), name) |
| } |
| |
| func findSymbol(fd *FileDescriptor, name string, public bool) Descriptor { |
| d := fd.symbols[name] |
| if d != nil { |
| return d |
| } |
| |
| // When public = false, we are searching only directly imported symbols. But we |
| // also need to search transitive public imports due to semantics of public imports. |
| var deps []*FileDescriptor |
| if public { |
| deps = fd.publicDeps |
| } else { |
| deps = fd.deps |
| } |
| for _, dep := range deps { |
| d = findSymbol(dep, name, true) |
| if d != nil { |
| return d |
| } |
| } |
| |
| return nil |
| } |
| |
| func merge(a, b string) string { |
| if a == "" { |
| return b |
| } else { |
| return a + "." + b |
| } |
| } |