-
-
Notifications
You must be signed in to change notification settings - Fork 99
Expand file tree
/
Copy pathreference.go
More file actions
349 lines (294 loc) · 10 KB
/
reference.go
File metadata and controls
349 lines (294 loc) · 10 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
package low
import (
"context"
"fmt"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/utils"
"go.yaml.in/yaml/v4"
)
const (
HASH = "%x"
)
type Reference struct {
refNode *yaml.Node
reference string
}
func (r Reference) GetReference() string {
return r.reference
}
func (r Reference) IsReference() bool {
return r.reference != ""
}
func (r Reference) GetReferenceNode() *yaml.Node {
if r.IsReference() && r.refNode == nil {
return utils.CreateRefNode(r.reference)
}
return r.refNode
}
func (r *Reference) SetReference(ref string, node *yaml.Node) {
r.reference = ref
r.refNode = node
}
type IsReferenced interface {
IsReference() bool
GetReference() string
GetReferenceNode() *yaml.Node
}
type SetReferencer interface {
SetReference(ref string, node *yaml.Node)
}
// Buildable is an interface for any struct that can be 'built out'. This means that a struct can accept
// a root node and a reference to the index that carries data about any references used.
//
// Used by generic functions when automatically building out structs based on yaml.Node inputs.
type Buildable[T any] interface {
Build(ctx context.Context, key, value *yaml.Node, idx *index.SpecIndex) error
*T
}
// HasValueNode is implemented by NodeReference and ValueReference to return the yaml.Node backing the value.
type HasValueNode[T any] interface {
GetValueNode() *yaml.Node
*T
}
// HasValueNodeUntyped is implemented by NodeReference and ValueReference to return the yaml.Node backing the value.
type HasValueNodeUntyped interface {
GetValueNode() *yaml.Node
IsReferenced
}
// Hashable defines any struct that implements a Hash function that returns a 64-bit hash of the state of the
// representative object. Great for equality checking!
type Hashable interface {
Hash() uint64
}
// HasExtensions is implemented by any object that exposes extensions
type HasExtensions[T any] interface {
// GetExtensions returns generic low level extensions
GetExtensions() *orderedmap.Map[KeyReference[string], ValueReference[*yaml.Node]]
}
// HasExtensionsUntyped is implemented by any object that exposes extensions
type HasExtensionsUntyped interface {
// GetExtensions returns generic low level extensions
GetExtensions() *orderedmap.Map[KeyReference[string], ValueReference[*yaml.Node]]
}
// HasValue is implemented by NodeReference and ValueReference to return the yaml.Node backing the value.
type HasValue[T any] interface {
GetValue() T
GetValueNode() *yaml.Node
IsEmpty() bool
*T
}
// HasValueUnTyped is implemented by NodeReference and ValueReference to return the yaml.Node backing the value.
type HasValueUnTyped interface {
GetValueUntyped() any
GetValueNode() *yaml.Node
}
// HasKeyNode is implemented by KeyReference to return the yaml.Node backing the key.
type HasKeyNode interface {
GetKeyNode() *yaml.Node
}
// NodeReference is a low-level container for holding a Value of type T, as well as references to
// a key yaml.Node that points to the key node that contains the value node, and the value node that contains
// the actual value.
type NodeReference[T any] struct {
Reference
// The value being referenced
Value T
// The yaml.Node that holds the value
ValueNode *yaml.Node
// The yaml.Node that is the key, that contains the value.
KeyNode *yaml.Node
Context context.Context
}
var _ HasValueNodeUntyped = &NodeReference[any]{}
// KeyReference is a low-level container for key nodes holding a Value of type T. A KeyNode is a pointer to the
// yaml.Node that holds a key to a value.
type KeyReference[T any] struct {
// The value being referenced.
Value T
// The yaml.Node that holds this referenced key
KeyNode *yaml.Node
}
// ValueReference is a low-level container for value nodes that hold a Value of type T. A ValueNode is a pointer
// to the yaml.Node that holds the value.
type ValueReference[T any] struct {
Reference
// The value being referenced.
Value T
// The yaml.Node that holds the referenced value
ValueNode *yaml.Node
}
// IsEmpty will return true if this reference has no key or value nodes assigned (it's been ignored)
func (n NodeReference[T]) IsEmpty() bool {
return n.KeyNode == nil && n.ValueNode == nil
}
func (n NodeReference[T]) NodeLineNumber() int {
if !n.IsEmpty() {
return n.ValueNode.Line
} else {
return 0
}
}
// GenerateMapKey will return a string based on the line and column number of the node, e.g. 33:56 for line 33, col 56.
func (n NodeReference[T]) GenerateMapKey() string {
return fmt.Sprintf("%d:%d", n.ValueNode.Line, n.ValueNode.Column)
}
// Mutate will set the reference value to what is supplied. This happens to both the Value and ValueNode, which means
// the root document is permanently mutated and changes will be reflected in any serialization of the root document.
func (n NodeReference[T]) Mutate(value T) NodeReference[T] {
n.ValueNode.Value = fmt.Sprintf("%v", value)
n.Value = value
return n
}
// GetValueNode will return the yaml.Node containing the reference value node
func (n NodeReference[T]) GetValueNode() *yaml.Node {
return n.ValueNode
}
// GetKeyNode will return the yaml.Node containing the reference key node
func (n NodeReference[T]) GetKeyNode() *yaml.Node {
return n.KeyNode
}
// GetValue will return the raw value of the node
func (n NodeReference[T]) GetValue() T {
return n.Value
}
// GetValueUntyped will return the raw value of the node with no type
func (n NodeReference[T]) GetValueUntyped() any {
return n.Value
}
// IsEmpty will return true if this reference has no key or value nodes assigned (it's been ignored)
func (n ValueReference[T]) IsEmpty() bool {
return n.ValueNode == nil
}
// NodeLineNumber will return the line number of the value node (or 0 if the value node is empty)
func (n ValueReference[T]) NodeLineNumber() int {
if !n.IsEmpty() {
return n.ValueNode.Line
} else {
return 0
}
}
// GenerateMapKey will return a string based on the line and column number of the node, e.g. 33:56 for line 33, col 56.
func (n ValueReference[T]) GenerateMapKey() string {
return fmt.Sprintf("%d:%d", n.ValueNode.Line, n.ValueNode.Column)
}
// GetValueNode will return the yaml.Node containing the reference value node
func (n ValueReference[T]) GetValueNode() *yaml.Node {
return n.ValueNode
}
// GetValue will return the raw value of the node
func (n ValueReference[T]) GetValue() T {
return n.Value
}
// GetValueUntyped will return the raw value of the node with no type
func (n ValueReference[T]) GetValueUntyped() any {
return n.Value
}
func (n ValueReference[T]) MarshalYAML() (interface{}, error) {
if n.IsReference() {
return n.GetReferenceNode(), nil
}
var h yaml.Node
e := n.ValueNode.Decode(&h)
return h, e
}
func (n KeyReference[T]) MarshalYAML() (interface{}, error) {
return n.KeyNode, nil
}
// IsEmpty will return true if this reference has no key or value nodes assigned (it's been ignored)
func (n KeyReference[T]) IsEmpty() bool {
return n.KeyNode == nil
}
// GetValueUntyped will return the raw value of the node with no type
func (n KeyReference[T]) GetValueUntyped() any {
return n.Value
}
// GetKeyNode will return the yaml.Node containing the reference key node.
func (n KeyReference[T]) GetKeyNode() *yaml.Node {
return n.KeyNode
}
// GenerateMapKey will return a string based on the line and column number of the node, e.g. 33:56 for line 33, col 56.
func (n KeyReference[T]) GenerateMapKey() string {
return fmt.Sprintf("%d:%d", n.KeyNode.Line, n.KeyNode.Column)
}
// Mutate will set the reference value to what is supplied. This happens to both the Value and ValueNode, which means
// the root document is permanently mutated and changes will be reflected in any serialization of the root document.
func (n ValueReference[T]) Mutate(value T) ValueReference[T] {
n.ValueNode.Value = fmt.Sprintf("%v", value)
n.Value = value
return n
}
// IsCircular will determine if the node in question, is part of a circular reference chain discovered by the index.
func IsCircular(node *yaml.Node, idx *index.SpecIndex) bool {
if idx == nil {
return false // no index! nothing we can do.
}
refs := idx.GetCircularReferences()
for i := range idx.GetCircularReferences() {
if refs[i].LoopPoint.Node == node {
return true
}
for k := range refs[i].Journey {
if refs[i].Journey[k].Node == node {
return true
}
isRef, _, refValue := utils.IsNodeRefValue(node)
if isRef && refs[i].Journey[k].Definition == refValue {
return true
}
}
}
// check mapped references in case we didn't find it.
_, nv := utils.FindKeyNode("$ref", node.Content)
if nv != nil {
ref := idx.GetMappedReferences()[nv.Value]
if ref != nil {
return ref.Circular
}
}
return false
}
// GetCircularReferenceResult will check if a node is part of a circular reference chain and then return that
// index.CircularReferenceResult it was located in. Returns nil if not found.
func GetCircularReferenceResult(node *yaml.Node, idx *index.SpecIndex) *index.CircularReferenceResult {
if idx == nil {
return nil // no index! nothing we can do.
}
var refs []*index.CircularReferenceResult
if idx.GetResolver() != nil {
refs = append(refs, idx.GetResolver().GetCircularReferences()...)
refs = append(refs, idx.GetResolver().GetInfiniteCircularReferences()...)
refs = append(refs, idx.GetResolver().GetIgnoredCircularArrayReferences()...)
refs = append(refs, idx.GetResolver().GetIgnoredCircularPolyReferences()...)
refs = append(refs, idx.GetResolver().GetSafeCircularReferences()...)
} else {
refs = idx.GetCircularReferences()
}
for i := range refs {
if refs[i].LoopPoint.Node == node {
return refs[i]
}
for k := range refs[i].Journey {
if refs[i].Journey[k].Node == node {
return refs[i]
}
isRef, _, refValue := utils.IsNodeRefValue(node)
if isRef && refs[i].Journey[k].Definition == refValue {
return refs[i]
}
}
}
// check mapped references in case we didn't find it.
_, nv := utils.FindKeyNode("$ref", node.Content)
if nv != nil {
for i := range refs {
if refs[i].LoopPoint.Definition == nv.Value {
return refs[i]
}
}
}
return nil
}
func HashToString(hash uint64) string {
return fmt.Sprintf("%x", hash)
}