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