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  }