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  }