opentofu/internal/plugin/convert/diagnostics.go
Martin Atkins b40a4fb741 Move plugin/ and plugin6/ to internal/plugin{,6}/
This is part of a general effort to move all of Terraform's non-library
package surface under internal in order to reinforce that these are for
internal use within Terraform only.

If you were previously importing packages under this prefix into an
external codebase, you could pin to an earlier release tag as an interim
solution until you've make a plan to achieve the same functionality some
other way.
2021-05-17 14:09:07 -07:00

133 lines
3.6 KiB
Go

package convert
import (
"github.com/hashicorp/terraform/internal/tfdiags"
proto "github.com/hashicorp/terraform/internal/tfplugin5"
"github.com/zclconf/go-cty/cty"
)
// WarnsAndErrorsToProto converts the warnings and errors return by the legacy
// provider to protobuf diagnostics.
func WarnsAndErrsToProto(warns []string, errs []error) (diags []*proto.Diagnostic) {
for _, w := range warns {
diags = AppendProtoDiag(diags, w)
}
for _, e := range errs {
diags = AppendProtoDiag(diags, e)
}
return diags
}
// AppendProtoDiag appends a new diagnostic from a warning string or an error.
// This panics if d is not a string or error.
func AppendProtoDiag(diags []*proto.Diagnostic, d interface{}) []*proto.Diagnostic {
switch d := d.(type) {
case cty.PathError:
ap := PathToAttributePath(d.Path)
diags = append(diags, &proto.Diagnostic{
Severity: proto.Diagnostic_ERROR,
Summary: d.Error(),
Attribute: ap,
})
case error:
diags = append(diags, &proto.Diagnostic{
Severity: proto.Diagnostic_ERROR,
Summary: d.Error(),
})
case string:
diags = append(diags, &proto.Diagnostic{
Severity: proto.Diagnostic_WARNING,
Summary: d,
})
case *proto.Diagnostic:
diags = append(diags, d)
case []*proto.Diagnostic:
diags = append(diags, d...)
}
return diags
}
// ProtoToDiagnostics converts a list of proto.Diagnostics to a tf.Diagnostics.
func ProtoToDiagnostics(ds []*proto.Diagnostic) tfdiags.Diagnostics {
var diags tfdiags.Diagnostics
for _, d := range ds {
var severity tfdiags.Severity
switch d.Severity {
case proto.Diagnostic_ERROR:
severity = tfdiags.Error
case proto.Diagnostic_WARNING:
severity = tfdiags.Warning
}
var newDiag tfdiags.Diagnostic
// if there's an attribute path, we need to create a AttributeValue diagnostic
if d.Attribute != nil && len(d.Attribute.Steps) > 0 {
path := AttributePathToPath(d.Attribute)
newDiag = tfdiags.AttributeValue(severity, d.Summary, d.Detail, path)
} else {
newDiag = tfdiags.WholeContainingBody(severity, d.Summary, d.Detail)
}
diags = diags.Append(newDiag)
}
return diags
}
// AttributePathToPath takes the proto encoded path and converts it to a cty.Path
func AttributePathToPath(ap *proto.AttributePath) cty.Path {
var p cty.Path
for _, step := range ap.Steps {
switch selector := step.Selector.(type) {
case *proto.AttributePath_Step_AttributeName:
p = p.GetAttr(selector.AttributeName)
case *proto.AttributePath_Step_ElementKeyString:
p = p.Index(cty.StringVal(selector.ElementKeyString))
case *proto.AttributePath_Step_ElementKeyInt:
p = p.Index(cty.NumberIntVal(selector.ElementKeyInt))
}
}
return p
}
// AttributePathToPath takes a cty.Path and converts it to a proto-encoded path.
func PathToAttributePath(p cty.Path) *proto.AttributePath {
ap := &proto.AttributePath{}
for _, step := range p {
switch selector := step.(type) {
case cty.GetAttrStep:
ap.Steps = append(ap.Steps, &proto.AttributePath_Step{
Selector: &proto.AttributePath_Step_AttributeName{
AttributeName: selector.Name,
},
})
case cty.IndexStep:
key := selector.Key
switch key.Type() {
case cty.String:
ap.Steps = append(ap.Steps, &proto.AttributePath_Step{
Selector: &proto.AttributePath_Step_ElementKeyString{
ElementKeyString: key.AsString(),
},
})
case cty.Number:
v, _ := key.AsBigFloat().Int64()
ap.Steps = append(ap.Steps, &proto.AttributePath_Step{
Selector: &proto.AttributePath_Step_ElementKeyInt{
ElementKeyInt: v,
},
})
default:
// We'll bail early if we encounter anything else, and just
// return the valid prefix.
return ap
}
}
}
return ap
}