1 // Copyright 2017, 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.md file.
16 // Path is a list of PathSteps describing the sequence of operations to get
17 // from some root type to the current position in the value tree.
18 // The first Path element is always an operation-less PathStep that exists
19 // simply to identify the initial type.
21 // When traversing structs with embedded structs, the embedded struct will
22 // always be accessed as a field before traversing the fields of the
23 // embedded struct themselves. That is, an exported field from the
24 // embedded struct will never be accessed directly from the parent struct.
27 // PathStep is a union-type for specific operations to traverse
28 // a value's tree structure. Users of this package never need to implement
29 // these types as values of this type will be returned by this package.
32 Type() reflect.Type // Resulting type after performing the path step
36 // SliceIndex is an index operation on a slice or array at some index Key.
37 SliceIndex interface {
39 Key() int // May return -1 if in a split state
41 // SplitKeys returns the indexes for indexing into slices in the
42 // x and y values, respectively. These indexes may differ due to the
43 // insertion or removal of an element in one of the slices, causing
44 // all of the indexes to be shifted. If an index is -1, then that
45 // indicates that the element does not exist in the associated slice.
47 // Key is guaranteed to return -1 if and only if the indexes returned
48 // by SplitKeys are not the same. SplitKeys will never return -1 for
50 SplitKeys() (x int, y int)
54 // MapIndex is an index operation on a map at some index Key.
60 // TypeAssertion represents a type assertion on an interface.
61 TypeAssertion interface {
65 // StructField represents a struct field access on a field called Name.
66 StructField interface {
72 // Indirect represents pointer indirection on the parent type.
77 // Transform is a transformation from the parent type to the current type.
83 // Option returns the originally constructed Transformer option.
84 // The == operator can be used to detect the exact option used.
91 func (pa *Path) push(s PathStep) {
95 func (pa *Path) pop() {
96 *pa = (*pa)[:len(*pa)-1]
99 // Last returns the last PathStep in the Path.
100 // If the path is empty, this returns a non-nil PathStep that reports a nil Type.
101 func (pa Path) Last() PathStep {
105 // Index returns the ith step in the Path and supports negative indexing.
106 // A negative index starts counting from the tail of the Path such that -1
107 // refers to the last step, -2 refers to the second-to-last step, and so on.
108 // If index is invalid, this returns a non-nil PathStep that reports a nil Type.
109 func (pa Path) Index(i int) PathStep {
113 if i < 0 || i >= len(pa) {
119 // String returns the simplified path to a node.
120 // The simplified path only contains struct field accesses.
123 // MyMap.MySlices.MyField
124 func (pa Path) String() string {
126 for _, s := range pa {
127 if _, ok := s.(*structField); ok {
128 ss = append(ss, s.String())
131 return strings.TrimPrefix(strings.Join(ss, ""), ".")
134 // GoString returns the path to a specific node using Go syntax.
137 // (*root.MyMap["key"].(*mypkg.MyStruct).MySlices)[2][3].MyField
138 func (pa Path) GoString() string {
139 var ssPre, ssPost []string
141 for i, s := range pa {
142 var nextStep PathStep
146 switch s := s.(type) {
149 pPre, pPost := "(", ")"
150 switch nextStep.(type) {
152 continue // Next step is indirection, so let them batch up
154 numIndirect-- // Automatic indirection on struct fields
156 pPre, pPost = "", "" // Last step; no need for parenthesis
159 ssPre = append(ssPre, pPre+strings.Repeat("*", numIndirect))
160 ssPost = append(ssPost, pPost)
165 ssPre = append(ssPre, s.trans.name+"(")
166 ssPost = append(ssPost, ")")
169 // As a special-case, elide type assertions on anonymous types
170 // since they are typically generated dynamically and can be very
171 // verbose. For example, some transforms return interface{} because
172 // of Go's lack of generics, but typically take in and return the
173 // exact same concrete type.
174 if s.Type().PkgPath() == "" {
178 ssPost = append(ssPost, s.String())
180 for i, j := 0, len(ssPre)-1; i < j; i, j = i+1, j-1 {
181 ssPre[i], ssPre[j] = ssPre[j], ssPre[i]
183 return strings.Join(ssPre, "") + strings.Join(ssPost, "")
199 typeAssertion struct {
207 // These fields are used for forcibly accessing an unexported field.
208 // pvx, pvy, and field are only valid if unexported is true.
210 force bool // Forcibly allow visibility
211 pvx, pvy reflect.Value // Parent values
212 field reflect.StructField // Field information
223 func (ps pathStep) Type() reflect.Type { return ps.typ }
224 func (ps pathStep) String() string {
229 if s == "" || strings.ContainsAny(s, "{}\n") {
230 return "root" // Type too simple or complex to print
232 return fmt.Sprintf("{%s}", s)
235 func (si sliceIndex) String() string {
237 case si.xkey == si.ykey:
238 return fmt.Sprintf("[%d]", si.xkey)
240 // [5->?] means "I don't know where X[5] went"
241 return fmt.Sprintf("[%d->?]", si.xkey)
243 // [?->3] means "I don't know where Y[3] came from"
244 return fmt.Sprintf("[?->%d]", si.ykey)
246 // [5->3] means "X[5] moved to Y[3]"
247 return fmt.Sprintf("[%d->%d]", si.xkey, si.ykey)
250 func (mi mapIndex) String() string { return fmt.Sprintf("[%#v]", mi.key) }
251 func (ta typeAssertion) String() string { return fmt.Sprintf(".(%v)", ta.typ) }
252 func (sf structField) String() string { return fmt.Sprintf(".%s", sf.name) }
253 func (in indirect) String() string { return "*" }
254 func (tf transform) String() string { return fmt.Sprintf("%s()", tf.trans.name) }
256 func (si sliceIndex) Key() int {
257 if si.xkey != si.ykey {
262 func (si sliceIndex) SplitKeys() (x, y int) { return si.xkey, si.ykey }
263 func (mi mapIndex) Key() reflect.Value { return mi.key }
264 func (sf structField) Name() string { return sf.name }
265 func (sf structField) Index() int { return sf.idx }
266 func (tf transform) Name() string { return tf.trans.name }
267 func (tf transform) Func() reflect.Value { return tf.trans.fnc }
268 func (tf transform) Option() Option { return tf.trans }
270 func (pathStep) isPathStep() {}
271 func (sliceIndex) isSliceIndex() {}
272 func (mapIndex) isMapIndex() {}
273 func (typeAssertion) isTypeAssertion() {}
274 func (structField) isStructField() {}
275 func (indirect) isIndirect() {}
276 func (transform) isTransform() {}
279 _ SliceIndex = sliceIndex{}
280 _ MapIndex = mapIndex{}
281 _ TypeAssertion = typeAssertion{}
282 _ StructField = structField{}
283 _ Indirect = indirect{}
284 _ Transform = transform{}
286 _ PathStep = sliceIndex{}
287 _ PathStep = mapIndex{}
288 _ PathStep = typeAssertion{}
289 _ PathStep = structField{}
290 _ PathStep = indirect{}
291 _ PathStep = transform{}
294 // isExported reports whether the identifier is exported.
295 func isExported(id string) bool {
296 r, _ := utf8.DecodeRuneInString(id)
297 return unicode.IsUpper(r)
300 // isValid reports whether the identifier is valid.
301 // Empty and underscore-only strings are not valid.
302 func isValid(id string) bool {
303 ok := id != "" && id != "_"
304 for j, c := range id {
305 ok = ok && (j > 0 || !unicode.IsDigit(c))
306 ok = ok && (c == '_' || unicode.IsLetter(c) || unicode.IsDigit(c))