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  }