github.com/hashicorp/hcl/v2@v2.20.0/cmd/hclspecsuite/traversals.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package main 5 6 import ( 7 "fmt" 8 "reflect" 9 10 "github.com/hashicorp/hcl/v2" 11 ) 12 13 func findTraversalSpec(got hcl.Traversal, candidates []*TestFileExpectTraversal) *TestFileExpectTraversal { 14 for _, candidate := range candidates { 15 if traversalsAreEquivalent(candidate.Traversal, got) { 16 return candidate 17 } 18 } 19 return nil 20 } 21 22 func findTraversalForSpec(want *TestFileExpectTraversal, have []hcl.Traversal) hcl.Traversal { 23 for _, candidate := range have { 24 if traversalsAreEquivalent(candidate, want.Traversal) { 25 return candidate 26 } 27 } 28 return nil 29 } 30 31 func traversalsAreEquivalent(a, b hcl.Traversal) bool { 32 if len(a) != len(b) { 33 return false 34 } 35 for i := range a { 36 aStep := a[i] 37 bStep := b[i] 38 39 if reflect.TypeOf(aStep) != reflect.TypeOf(bStep) { 40 return false 41 } 42 43 // We can now assume that both are of the same type. 44 switch ts := aStep.(type) { 45 46 case hcl.TraverseRoot: 47 if bStep.(hcl.TraverseRoot).Name != ts.Name { 48 return false 49 } 50 51 case hcl.TraverseAttr: 52 if bStep.(hcl.TraverseAttr).Name != ts.Name { 53 return false 54 } 55 56 case hcl.TraverseIndex: 57 if !bStep.(hcl.TraverseIndex).Key.RawEquals(ts.Key) { 58 return false 59 } 60 61 default: 62 return false 63 } 64 } 65 return true 66 } 67 68 // checkTraversalsMatch determines if a given traversal matches the given 69 // expectation, which must've been produced by an earlier call to 70 // findTraversalSpec for the same traversal. 71 func checkTraversalsMatch(got hcl.Traversal, filename string, match *TestFileExpectTraversal) hcl.Diagnostics { 72 var diags hcl.Diagnostics 73 74 gotRng := got.SourceRange() 75 wantRng := match.Range 76 77 if got, want := gotRng.Filename, filename; got != want { 78 diags = append(diags, &hcl.Diagnostic{ 79 Severity: hcl.DiagError, 80 Summary: "Incorrect filename in detected traversal", 81 Detail: fmt.Sprintf( 82 "Filename was reported as %q, but was expecting %q.", 83 got, want, 84 ), 85 Subject: match.Traversal.SourceRange().Ptr(), 86 }) 87 return diags 88 } 89 90 // If we have the expected filename then we'll use that to construct the 91 // full "want range" here so that we can use it to point to the appropriate 92 // location in the remaining diagnostics. 93 wantRng.Filename = filename 94 95 if got, want := gotRng.Start, wantRng.Start; got != want { 96 diags = append(diags, &hcl.Diagnostic{ 97 Severity: hcl.DiagError, 98 Summary: "Incorrect start position in detected traversal", 99 Detail: fmt.Sprintf( 100 "Start position was reported as line %d column %d byte %d, but was expecting line %d column %d byte %d.", 101 got.Line, got.Column, got.Byte, 102 want.Line, want.Column, want.Byte, 103 ), 104 Subject: &wantRng, 105 }) 106 } 107 if got, want := gotRng.End, wantRng.End; got != want { 108 diags = append(diags, &hcl.Diagnostic{ 109 Severity: hcl.DiagError, 110 Summary: "Incorrect end position in detected traversal", 111 Detail: fmt.Sprintf( 112 "End position was reported as line %d column %d byte %d, but was expecting line %d column %d byte %d.", 113 got.Line, got.Column, got.Byte, 114 want.Line, want.Column, want.Byte, 115 ), 116 Subject: &wantRng, 117 }) 118 } 119 return diags 120 }