From 9bd47bf17c8b96f57a46724af3ec23b5b2eb346a Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Fri, 16 Mar 2018 18:30:06 -0700 Subject: [PATCH] tfdiags: helper functions for nicer display of cty.PathError cty doesn't have a native string representation of a cty.Path because it is the layer below any particular syntax, but up here in Terraform land we know that we use HCL native syntax and so we can format a string in a HCL-ish way for familiarity to the user. We'll use this form automatically when such an error is used directly as a diagnostic, but we also expose the function publicly so that other code that incorporates errors into diagnostic detail strings can apply the same formatting. --- tfdiags/config_traversals.go | 56 ++++++++++++++++++++++++++++++++++++ tfdiags/error.go | 2 +- 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 tfdiags/config_traversals.go diff --git a/tfdiags/config_traversals.go b/tfdiags/config_traversals.go new file mode 100644 index 0000000000..1b6b2441d9 --- /dev/null +++ b/tfdiags/config_traversals.go @@ -0,0 +1,56 @@ +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()) +} diff --git a/tfdiags/error.go b/tfdiags/error.go index 35edc3041b..4ad6b0567d 100644 --- a/tfdiags/error.go +++ b/tfdiags/error.go @@ -13,7 +13,7 @@ func (e nativeError) Severity() Severity { func (e nativeError) Description() Description { return Description{ - Summary: e.err.Error(), + Summary: FormatError(e.err), } }