github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/tfdiags/config_traversals.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package tfdiags 5 6 import ( 7 "bytes" 8 "fmt" 9 "strconv" 10 11 "github.com/zclconf/go-cty/cty" 12 ) 13 14 // FormatCtyPath is a helper function to produce a user-friendly string 15 // representation of a cty.Path. The result uses a syntax similar to the 16 // HCL expression language in the hope of it being familiar to users. 17 func FormatCtyPath(path cty.Path) string { 18 var buf bytes.Buffer 19 for _, step := range path { 20 switch ts := step.(type) { 21 case cty.GetAttrStep: 22 fmt.Fprintf(&buf, ".%s", ts.Name) 23 case cty.IndexStep: 24 buf.WriteByte('[') 25 key := ts.Key 26 keyTy := key.Type() 27 switch { 28 case key.IsNull(): 29 buf.WriteString("null") 30 case !key.IsKnown(): 31 buf.WriteString("(not yet known)") 32 case keyTy == cty.Number: 33 bf := key.AsBigFloat() 34 buf.WriteString(bf.Text('g', -1)) 35 case keyTy == cty.String: 36 buf.WriteString(strconv.Quote(key.AsString())) 37 default: 38 buf.WriteString("...") 39 } 40 buf.WriteByte(']') 41 } 42 } 43 return buf.String() 44 } 45 46 // FormatError is a helper function to produce a user-friendly string 47 // representation of certain special error types that we might want to 48 // include in diagnostic messages. 49 // 50 // This currently has special behavior only for cty.PathError, where a 51 // non-empty path is rendered in a HCL-like syntax as context. 52 func FormatError(err error) string { 53 perr, ok := err.(cty.PathError) 54 if !ok || len(perr.Path) == 0 { 55 return err.Error() 56 } 57 58 return fmt.Sprintf("%s: %s", FormatCtyPath(perr.Path), perr.Error()) 59 } 60 61 // FormatErrorPrefixed is like FormatError except that it presents any path 62 // information after the given prefix string, which is assumed to contain 63 // an HCL syntax representation of the value that errors are relative to. 64 func FormatErrorPrefixed(err error, prefix string) string { 65 perr, ok := err.(cty.PathError) 66 if !ok || len(perr.Path) == 0 { 67 return fmt.Sprintf("%s: %s", prefix, err.Error()) 68 } 69 70 return fmt.Sprintf("%s%s: %s", prefix, FormatCtyPath(perr.Path), perr.Error()) 71 }