package tfdiags import ( "bytes" "fmt" "strconv" "github.com/zclconf/go-cty/cty" ) // FormatCtyPath is a helper function to produce a user-friendly string // representation of a cty.Path. The result uses a syntax similar to the // HCL expression language in the hope of it being familiar to users. func FormatCtyPath(path cty.Path) string { var buf bytes.Buffer for _, step := range path { switch ts := step.(type) { case cty.GetAttrStep: fmt.Fprintf(&buf, ".%s", ts.Name) case cty.IndexStep: buf.WriteByte('[') key := ts.Key keyTy := key.Type() switch { case key.IsNull(): buf.WriteString("null") case !key.IsKnown(): buf.WriteString("(not yet known)") case keyTy == cty.Number: bf := key.AsBigFloat() buf.WriteString(bf.Text('g', -1)) case keyTy == cty.String: buf.WriteString(strconv.Quote(key.AsString())) default: buf.WriteString("...") } buf.WriteByte(']') } } return buf.String() } // FormatError is a helper function to produce a user-friendly string // representation of certain special error types that we might want to // include in diagnostic messages. // // This currently has special behavior only for cty.PathError, where a // non-empty path is rendered in a HCL-like syntax as context. func FormatError(err error) string { perr, ok := err.(cty.PathError) if !ok || len(perr.Path) == 0 { return err.Error() } return fmt.Sprintf("%s: %s", FormatCtyPath(perr.Path), perr.Error()) } // FormatErrorPrefixed is like FormatError except that it presents any path // information after the given prefix string, which is assumed to contain // an HCL syntax representation of the value that errors are relative to. func FormatErrorPrefixed(err error, prefix string) string { perr, ok := err.(cty.PathError) if !ok || len(perr.Path) == 0 { return fmt.Sprintf("%s: %s", prefix, err.Error()) } return fmt.Sprintf("%s%s: %s", prefix, FormatCtyPath(perr.Path), perr.Error()) }