@@ -7,16 +7,22 @@ import (
77 "github.com/databricks/cli/libs/structdiff/jsontag"
88)
99
10+ const (
11+ tagStruct = - 1
12+ tagMapKey = - 2
13+ tagUnresolvedStruct = - 3
14+ tagAnyKey = - 4
15+ tagAnyIndex = - 5
16+ )
17+
1018// PathNode represents a node in a path for struct diffing.
1119// It can represent struct fields, map keys, or array/slice indices.
1220type PathNode struct {
1321 prev * PathNode
1422 jsonTag jsontag.JSONTag // For lazy JSON key resolution
1523 key string // Computed key (JSON key for structs, string key for maps, or Go field name for fallback)
1624 // If index >= 0, the node specifies a slice/array index in index.
17- // If index == -1, the node specifies a struct attribute
18- // If index == -2, the node specifies a map key in key
19- // If index == -3, the node specifies an unresolved struct attribute
25+ // If index < 0, this describes the type of node (see tagStruct and other consts above)
2026 index int
2127}
2228
@@ -42,21 +48,35 @@ func (p *PathNode) MapKey() (string, bool) {
4248 if p == nil {
4349 return "" , false
4450 }
45- if p .index == - 2 {
51+ if p .index == tagMapKey {
4652 return p .key , true
4753 }
4854 return "" , false
4955}
5056
57+ func (p * PathNode ) AnyKey () bool {
58+ if p == nil {
59+ return false
60+ }
61+ return p .index == tagAnyKey
62+ }
63+
64+ func (p * PathNode ) AnyIndex () bool {
65+ if p == nil {
66+ return false
67+ }
68+ return p .index == tagAnyIndex
69+ }
70+
5171func (p * PathNode ) resolveField () {
52- if p .index == - 3 {
72+ if p .index == tagUnresolvedStruct {
5373 // Lazy resolve JSON key for struct fields
5474 jsonName := p .jsonTag .Name ()
5575 if jsonName != "" {
5676 p .key = jsonName
5777 }
5878 // If jsonName is empty, key already contains the Go field name as fallback
59- p .index = - 1
79+ p .index = tagStruct
6080 }
6181}
6282
@@ -65,14 +85,17 @@ func (p *PathNode) Field() (string, bool) {
6585 return "" , false
6686 }
6787 p .resolveField ()
68- if p .index == - 1 {
88+ if p .index == tagStruct {
6989 return p .key , true
7090 }
7191 return "" , false
7292}
7393
7494// NewIndex creates a new PathNode for an array/slice index.
7595func NewIndex (prev * PathNode , index int ) * PathNode {
96+ if index < 0 {
97+ panic ("index msut be non-negative" )
98+ }
7699 return & PathNode {
77100 prev : prev ,
78101 index : index ,
@@ -84,7 +107,7 @@ func NewMapKey(prev *PathNode, key string) *PathNode {
84107 return & PathNode {
85108 prev : prev ,
86109 key : key ,
87- index : - 2 ,
110+ index : tagMapKey ,
88111 }
89112}
90113
@@ -95,7 +118,21 @@ func NewStructField(prev *PathNode, jsonTag jsontag.JSONTag, fieldName string) *
95118 prev : prev ,
96119 jsonTag : jsonTag ,
97120 key : fieldName ,
98- index : - 3 , // Unresolved struct attribute
121+ index : tagUnresolvedStruct ,
122+ }
123+ }
124+
125+ func NewAnyKey (prev * PathNode ) * PathNode {
126+ return & PathNode {
127+ prev : prev ,
128+ index : tagAnyKey ,
129+ }
130+ }
131+
132+ func NewAnyIndex (prev * PathNode ) * PathNode {
133+ return & PathNode {
134+ prev : prev ,
135+ index : tagAnyIndex ,
99136 }
100137}
101138
@@ -109,9 +146,13 @@ func (p *PathNode) String() string {
109146 return p .prev .String () + "[" + strconv .Itoa (p .index ) + "]"
110147 }
111148
149+ if p .index == tagAnyKey || p .index == tagAnyIndex {
150+ return p .prev .String () + "[*]"
151+ }
152+
112153 p .resolveField ()
113154
114- if p .index == - 1 {
155+ if p .index == tagStruct {
115156 return p .prev .String () + "." + p .key
116157 }
117158
@@ -128,6 +169,19 @@ func (p *PathNode) DynPath() string {
128169 return p .prev .DynPath () + "[" + strconv .Itoa (p .index ) + "]"
129170 }
130171
172+ if p .index == tagAnyKey {
173+ prev := p .prev .DynPath ()
174+ if prev == "" {
175+ return "*"
176+ } else {
177+ return prev + ".*"
178+ }
179+ }
180+
181+ if p .index == tagAnyIndex {
182+ return p .prev .DynPath () + "[*]"
183+ }
184+
131185 p .resolveField ()
132186
133187 prev := p .prev .DynPath ()
0 commit comments