| // Copyright 2020 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package protopath |
| |
| import ( |
| "fmt" |
| "strconv" |
| "strings" |
| |
| "google.golang.org/protobuf/internal/encoding/text" |
| "google.golang.org/protobuf/reflect/protoreflect" |
| ) |
| |
| // StepKind identifies the kind of step operation. |
| // Each kind of step corresponds with some protobuf reflection operation. |
| type StepKind int |
| |
| const ( |
| invalidStep StepKind = iota |
| // RootStep identifies a step as the Root step operation. |
| RootStep |
| // FieldAccessStep identifies a step as the FieldAccess step operation. |
| FieldAccessStep |
| // UnknownAccessStep identifies a step as the UnknownAccess step operation. |
| UnknownAccessStep |
| // ListIndexStep identifies a step as the ListIndex step operation. |
| ListIndexStep |
| // MapIndexStep identifies a step as the MapIndex step operation. |
| MapIndexStep |
| // AnyExpandStep identifies a step as the AnyExpand step operation. |
| AnyExpandStep |
| ) |
| |
| func (k StepKind) String() string { |
| switch k { |
| case invalidStep: |
| return "<invalid>" |
| case RootStep: |
| return "Root" |
| case FieldAccessStep: |
| return "FieldAccess" |
| case UnknownAccessStep: |
| return "UnknownAccess" |
| case ListIndexStep: |
| return "ListIndex" |
| case MapIndexStep: |
| return "MapIndex" |
| case AnyExpandStep: |
| return "AnyExpand" |
| default: |
| return fmt.Sprintf("<unknown:%d>", k) |
| } |
| } |
| |
| // Step is a union where only one step operation may be specified at a time. |
| // The different kinds of steps are specified by the constants defined for |
| // the StepKind type. |
| type Step struct { |
| kind StepKind |
| desc protoreflect.Descriptor |
| key protoreflect.Value |
| } |
| |
| // Root indicates the root message that a path is relative to. |
| // It should always (and only ever) be the first step in a path. |
| func Root(md protoreflect.MessageDescriptor) Step { |
| if md == nil { |
| panic("nil message descriptor") |
| } |
| return Step{kind: RootStep, desc: md} |
| } |
| |
| // FieldAccess describes access of a field within a message. |
| // Extension field accesses are also represented using a FieldAccess and |
| // must be provided with a protoreflect.FieldDescriptor |
| // |
| // Within the context of Values, |
| // the type of the previous step value is always a message, and |
| // the type of the current step value is determined by the field descriptor. |
| func FieldAccess(fd protoreflect.FieldDescriptor) Step { |
| if fd == nil { |
| panic("nil field descriptor") |
| } else if _, ok := fd.(protoreflect.ExtensionTypeDescriptor); !ok && fd.IsExtension() { |
| panic(fmt.Sprintf("extension field %q must implement protoreflect.ExtensionTypeDescriptor", fd.FullName())) |
| } |
| return Step{kind: FieldAccessStep, desc: fd} |
| } |
| |
| // UnknownAccess describes access to the unknown fields within a message. |
| // |
| // Within the context of Values, |
| // the type of the previous step value is always a message, and |
| // the type of the current step value is always a bytes type. |
| func UnknownAccess() Step { |
| return Step{kind: UnknownAccessStep} |
| } |
| |
| // ListIndex describes index of an element within a list. |
| // |
| // Within the context of Values, |
| // the type of the previous, previous step value is always a message, |
| // the type of the previous step value is always a list, and |
| // the type of the current step value is determined by the field descriptor. |
| func ListIndex(i int) Step { |
| if i < 0 { |
| panic(fmt.Sprintf("invalid list index: %v", i)) |
| } |
| return Step{kind: ListIndexStep, key: protoreflect.ValueOfInt64(int64(i))} |
| } |
| |
| // MapIndex describes index of an entry within a map. |
| // The key type is determined by field descriptor that the map belongs to. |
| // |
| // Within the context of Values, |
| // the type of the previous previous step value is always a message, |
| // the type of the previous step value is always a map, and |
| // the type of the current step value is determined by the field descriptor. |
| func MapIndex(k protoreflect.MapKey) Step { |
| if !k.IsValid() { |
| panic("invalid map index") |
| } |
| return Step{kind: MapIndexStep, key: k.Value()} |
| } |
| |
| // AnyExpand describes expansion of a google.protobuf.Any message into |
| // a structured representation of the underlying message. |
| // |
| // Within the context of Values, |
| // the type of the previous step value is always a google.protobuf.Any message, and |
| // the type of the current step value is always a message. |
| func AnyExpand(md protoreflect.MessageDescriptor) Step { |
| if md == nil { |
| panic("nil message descriptor") |
| } |
| return Step{kind: AnyExpandStep, desc: md} |
| } |
| |
| // MessageDescriptor returns the message descriptor for Root or AnyExpand steps, |
| // otherwise it returns nil. |
| func (s Step) MessageDescriptor() protoreflect.MessageDescriptor { |
| switch s.kind { |
| case RootStep, AnyExpandStep: |
| return s.desc.(protoreflect.MessageDescriptor) |
| default: |
| return nil |
| } |
| } |
| |
| // FieldDescriptor returns the field descriptor for FieldAccess steps, |
| // otherwise it returns nil. |
| func (s Step) FieldDescriptor() protoreflect.FieldDescriptor { |
| switch s.kind { |
| case FieldAccessStep: |
| return s.desc.(protoreflect.FieldDescriptor) |
| default: |
| return nil |
| } |
| } |
| |
| // ListIndex returns the list index for ListIndex steps, |
| // otherwise it returns 0. |
| func (s Step) ListIndex() int { |
| switch s.kind { |
| case ListIndexStep: |
| return int(s.key.Int()) |
| default: |
| return 0 |
| } |
| } |
| |
| // MapIndex returns the map key for MapIndex steps, |
| // otherwise it returns an invalid map key. |
| func (s Step) MapIndex() protoreflect.MapKey { |
| switch s.kind { |
| case MapIndexStep: |
| return s.key.MapKey() |
| default: |
| return protoreflect.MapKey{} |
| } |
| } |
| |
| // Kind reports which kind of step this is. |
| func (s Step) Kind() StepKind { |
| return s.kind |
| } |
| |
| func (s Step) String() string { |
| return string(s.appendString(nil)) |
| } |
| |
| func (s Step) appendString(b []byte) []byte { |
| switch s.kind { |
| case RootStep: |
| b = append(b, '(') |
| b = append(b, s.desc.FullName()...) |
| b = append(b, ')') |
| case FieldAccessStep: |
| b = append(b, '.') |
| if fd := s.desc.(protoreflect.FieldDescriptor); fd.IsExtension() { |
| b = append(b, '(') |
| b = append(b, strings.Trim(fd.TextName(), "[]")...) |
| b = append(b, ')') |
| } else { |
| b = append(b, fd.TextName()...) |
| } |
| case UnknownAccessStep: |
| b = append(b, '.') |
| b = append(b, '?') |
| case ListIndexStep: |
| b = append(b, '[') |
| b = strconv.AppendInt(b, s.key.Int(), 10) |
| b = append(b, ']') |
| case MapIndexStep: |
| b = append(b, '[') |
| switch k := s.key.Interface().(type) { |
| case bool: |
| b = strconv.AppendBool(b, bool(k)) // e.g., "true" or "false" |
| case int32: |
| b = strconv.AppendInt(b, int64(k), 10) // e.g., "-32" |
| case int64: |
| b = strconv.AppendInt(b, int64(k), 10) // e.g., "-64" |
| case uint32: |
| b = strconv.AppendUint(b, uint64(k), 10) // e.g., "32" |
| case uint64: |
| b = strconv.AppendUint(b, uint64(k), 10) // e.g., "64" |
| case string: |
| b = text.AppendString(b, k) // e.g., `"hello, world"` |
| } |
| b = append(b, ']') |
| case AnyExpandStep: |
| b = append(b, '.') |
| b = append(b, '(') |
| b = append(b, s.desc.FullName()...) |
| b = append(b, ')') |
| default: |
| b = append(b, "<invalid>"...) |
| } |
| return b |
| } |