...

Source file src/google.golang.org/protobuf/reflect/protopath/path.go

Documentation: google.golang.org/protobuf/reflect/protopath

     1  // Copyright 2020 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package protopath provides functionality for
     6  // representing a sequence of protobuf reflection operations on a message.
     7  package protopath
     8  
     9  import (
    10  	"fmt"
    11  
    12  	"google.golang.org/protobuf/internal/msgfmt"
    13  	"google.golang.org/protobuf/reflect/protoreflect"
    14  )
    15  
    16  // NOTE: The Path and Values are separate types here since there are use cases
    17  // where you would like to "address" some value in a message with just the path
    18  // and don't have the value information available.
    19  //
    20  // This is different from how github.com/google/go-cmp/cmp.Path operates,
    21  // which combines both path and value information together.
    22  // Since the cmp package itself is the only one ever constructing a cmp.Path,
    23  // it will always have the value available.
    24  
    25  // Path is a sequence of protobuf reflection steps applied to some root
    26  // protobuf message value to arrive at the current value.
    27  // The first step must be a [Root] step.
    28  type Path []Step
    29  
    30  // TODO: Provide a Parse function that parses something similar to or
    31  // perhaps identical to the output of Path.String.
    32  
    33  // Index returns the ith step in the path and supports negative indexing.
    34  // A negative index starts counting from the tail of the Path such that -1
    35  // refers to the last step, -2 refers to the second-to-last step, and so on.
    36  // It returns a zero Step value if the index is out-of-bounds.
    37  func (p Path) Index(i int) Step {
    38  	if i < 0 {
    39  		i = len(p) + i
    40  	}
    41  	if i < 0 || i >= len(p) {
    42  		return Step{}
    43  	}
    44  	return p[i]
    45  }
    46  
    47  // String returns a structured representation of the path
    48  // by concatenating the string representation of every path step.
    49  func (p Path) String() string {
    50  	var b []byte
    51  	for _, s := range p {
    52  		b = s.appendString(b)
    53  	}
    54  	return string(b)
    55  }
    56  
    57  // Values is a Path paired with a sequence of values at each step.
    58  // The lengths of [Values.Path] and [Values.Values] must be identical.
    59  // The first step must be a [Root] step and
    60  // the first value must be a concrete message value.
    61  type Values struct {
    62  	Path   Path
    63  	Values []protoreflect.Value
    64  }
    65  
    66  // Len reports the length of the path and values.
    67  // If the path and values have differing length, it returns the minimum length.
    68  func (p Values) Len() int {
    69  	n := len(p.Path)
    70  	if n > len(p.Values) {
    71  		n = len(p.Values)
    72  	}
    73  	return n
    74  }
    75  
    76  // Index returns the ith step and value and supports negative indexing.
    77  // A negative index starts counting from the tail of the Values such that -1
    78  // refers to the last pair, -2 refers to the second-to-last pair, and so on.
    79  func (p Values) Index(i int) (out struct {
    80  	Step  Step
    81  	Value protoreflect.Value
    82  }) {
    83  	// NOTE: This returns a single struct instead of two return values so that
    84  	// callers can make use of the the value in an expression:
    85  	//	vs.Index(i).Value.Interface()
    86  	n := p.Len()
    87  	if i < 0 {
    88  		i = n + i
    89  	}
    90  	if i < 0 || i >= n {
    91  		return out
    92  	}
    93  	out.Step = p.Path[i]
    94  	out.Value = p.Values[i]
    95  	return out
    96  }
    97  
    98  // String returns a humanly readable representation of the path and last value.
    99  // Do not depend on the output being stable.
   100  //
   101  // For example:
   102  //
   103  //	(path.to.MyMessage).list_field[5].map_field["hello"] = {hello: "world"}
   104  func (p Values) String() string {
   105  	n := p.Len()
   106  	if n == 0 {
   107  		return ""
   108  	}
   109  
   110  	// Determine the field descriptor associated with the last step.
   111  	var fd protoreflect.FieldDescriptor
   112  	last := p.Index(-1)
   113  	switch last.Step.kind {
   114  	case FieldAccessStep:
   115  		fd = last.Step.FieldDescriptor()
   116  	case MapIndexStep, ListIndexStep:
   117  		fd = p.Index(-2).Step.FieldDescriptor()
   118  	}
   119  
   120  	// Format the full path with the last value.
   121  	return fmt.Sprintf("%v = %v", p.Path[:n], msgfmt.FormatValue(last.Value, fd))
   122  }
   123  

View as plain text