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