123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122 |
- // 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 provides functionality for
- // representing a sequence of protobuf reflection operations on a message.
- package protopath
- import (
- "fmt"
- "google.golang.org/protobuf/internal/msgfmt"
- "google.golang.org/protobuf/reflect/protoreflect"
- )
- // NOTE: The Path and Values are separate types here since there are use cases
- // where you would like to "address" some value in a message with just the path
- // and don't have the value information available.
- //
- // This is different from how github.com/google/go-cmp/cmp.Path operates,
- // which combines both path and value information together.
- // Since the cmp package itself is the only one ever constructing a cmp.Path,
- // it will always have the value available.
- // Path is a sequence of protobuf reflection steps applied to some root
- // protobuf message value to arrive at the current value.
- // The first step must be a [Root] step.
- type Path []Step
- // TODO: Provide a Parse function that parses something similar to or
- // perhaps identical to the output of Path.String.
- // Index returns the ith step in the path and supports negative indexing.
- // A negative index starts counting from the tail of the Path such that -1
- // refers to the last step, -2 refers to the second-to-last step, and so on.
- // It returns a zero Step value if the index is out-of-bounds.
- func (p Path) Index(i int) Step {
- if i < 0 {
- i = len(p) + i
- }
- if i < 0 || i >= len(p) {
- return Step{}
- }
- return p[i]
- }
- // String returns a structured representation of the path
- // by concatenating the string representation of every path step.
- func (p Path) String() string {
- var b []byte
- for _, s := range p {
- b = s.appendString(b)
- }
- return string(b)
- }
- // Values is a Path paired with a sequence of values at each step.
- // The lengths of [Values.Path] and [Values.Values] must be identical.
- // The first step must be a [Root] step and
- // the first value must be a concrete message value.
- type Values struct {
- Path Path
- Values []protoreflect.Value
- }
- // Len reports the length of the path and values.
- // If the path and values have differing length, it returns the minimum length.
- func (p Values) Len() int {
- n := len(p.Path)
- if n > len(p.Values) {
- n = len(p.Values)
- }
- return n
- }
- // Index returns the ith step and value and supports negative indexing.
- // A negative index starts counting from the tail of the Values such that -1
- // refers to the last pair, -2 refers to the second-to-last pair, and so on.
- func (p Values) Index(i int) (out struct {
- Step Step
- Value protoreflect.Value
- }) {
- // NOTE: This returns a single struct instead of two return values so that
- // callers can make use of the the value in an expression:
- // vs.Index(i).Value.Interface()
- n := p.Len()
- if i < 0 {
- i = n + i
- }
- if i < 0 || i >= n {
- return out
- }
- out.Step = p.Path[i]
- out.Value = p.Values[i]
- return out
- }
- // String returns a humanly readable representation of the path and last value.
- // Do not depend on the output being stable.
- //
- // For example:
- //
- // (path.to.MyMessage).list_field[5].map_field["hello"] = {hello: "world"}
- func (p Values) String() string {
- n := p.Len()
- if n == 0 {
- return ""
- }
- // Determine the field descriptor associated with the last step.
- var fd protoreflect.FieldDescriptor
- last := p.Index(-1)
- switch last.Step.kind {
- case FieldAccessStep:
- fd = last.Step.FieldDescriptor()
- case MapIndexStep, ListIndexStep:
- fd = p.Index(-2).Step.FieldDescriptor()
- }
- // Format the full path with the last value.
- return fmt.Sprintf("%v = %v", p.Path[:n], msgfmt.FormatValue(last.Value, fd))
- }
|