1package addrs 2 3import ( 4 "fmt" 5 6 "github.com/hashicorp/hcl/v2" 7) 8 9// Reference describes a reference to an address with source location 10// information. 11type Reference struct { 12 Subject Referenceable 13 SourceRange hcl.Range 14 Remaining hcl.Traversal 15} 16 17// ParseRef attempts to extract a referencable address from the prefix of the 18// given traversal, which must be an absolute traversal or this function 19// will panic. 20// 21// If no error diagnostics are returned, the returned reference includes the 22// address that was extracted, the source range it was extracted from, and any 23// remaining relative traversal that was not consumed as part of the 24// reference. 25// 26// If error diagnostics are returned then the Reference value is invalid and 27// must not be used. 28func ParseRef(traversal hcl.Traversal) (*Reference, hcl.Diagnostics) { 29 ref, diags := parseRef(traversal) 30 31 // Normalize a little to make life easier for callers. 32 if ref != nil { 33 if len(ref.Remaining) == 0 { 34 ref.Remaining = nil 35 } 36 } 37 38 return ref, diags 39} 40 41func parseRef(traversal hcl.Traversal) (*Reference, hcl.Diagnostics) { 42 var diags hcl.Diagnostics 43 44 root := traversal.RootName() 45 rootRange := traversal[0].SourceRange() 46 47 switch root { 48 49 case "var": 50 name, rng, remain, diags := parseSingleAttrRef(traversal) 51 return &Reference{ 52 Subject: InputVariable{Name: name}, 53 SourceRange: rng, 54 Remaining: remain, 55 }, diags 56 57 default: 58 diags = append(diags, &hcl.Diagnostic{ 59 Severity: hcl.DiagError, 60 Summary: "Unhandled reference type", 61 Detail: `Currently parseRef can only parse "var" references.`, 62 Subject: &rootRange, 63 }) 64 } 65 return nil, diags 66} 67 68func parseSingleAttrRef(traversal hcl.Traversal) (string, hcl.Range, hcl.Traversal, hcl.Diagnostics) { 69 var diags hcl.Diagnostics 70 71 root := traversal.RootName() 72 rootRange := traversal[0].SourceRange() 73 74 if len(traversal) < 2 { 75 diags = append(diags, &hcl.Diagnostic{ 76 Severity: hcl.DiagError, 77 Summary: "Invalid reference", 78 Detail: fmt.Sprintf("The %q object cannot be accessed directly. Instead, access one of its attributes.", root), 79 Subject: &rootRange, 80 }) 81 return "", hcl.Range{}, nil, diags 82 } 83 if attrTrav, ok := traversal[1].(hcl.TraverseAttr); ok { 84 return attrTrav.Name, hcl.RangeBetween(rootRange, attrTrav.SrcRange), traversal[2:], diags 85 } 86 diags = diags.Append(&hcl.Diagnostic{ 87 Severity: hcl.DiagError, 88 Summary: "Invalid reference", 89 Detail: fmt.Sprintf("The %q object does not support this operation.", root), 90 Subject: traversal[1].SourceRange().Ptr(), 91 }) 92 return "", hcl.Range{}, nil, diags 93} 94