aboutsummaryrefslogtreecommitdiff
path: root/reflect/protopath/step.go
diff options
context:
space:
mode:
Diffstat (limited to 'reflect/protopath/step.go')
-rw-r--r--reflect/protopath/step.go241
1 files changed, 241 insertions, 0 deletions
diff --git a/reflect/protopath/step.go b/reflect/protopath/step.go
new file mode 100644
index 00000000..95ae85c5
--- /dev/null
+++ b/reflect/protopath/step.go
@@ -0,0 +1,241 @@
+// 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
+}