github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/plugin6/convert/diagnostics.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package convert 5 6 import ( 7 "github.com/terramate-io/tf/tfdiags" 8 proto "github.com/terramate-io/tf/tfplugin6" 9 "github.com/zclconf/go-cty/cty" 10 ) 11 12 // WarnsAndErrorsToProto converts the warnings and errors return by the legacy 13 // provider to protobuf diagnostics. 14 func WarnsAndErrsToProto(warns []string, errs []error) (diags []*proto.Diagnostic) { 15 for _, w := range warns { 16 diags = AppendProtoDiag(diags, w) 17 } 18 19 for _, e := range errs { 20 diags = AppendProtoDiag(diags, e) 21 } 22 23 return diags 24 } 25 26 // AppendProtoDiag appends a new diagnostic from a warning string or an error. 27 // This panics if d is not a string or error. 28 func AppendProtoDiag(diags []*proto.Diagnostic, d interface{}) []*proto.Diagnostic { 29 switch d := d.(type) { 30 case cty.PathError: 31 ap := PathToAttributePath(d.Path) 32 diags = append(diags, &proto.Diagnostic{ 33 Severity: proto.Diagnostic_ERROR, 34 Summary: d.Error(), 35 Attribute: ap, 36 }) 37 case error: 38 diags = append(diags, &proto.Diagnostic{ 39 Severity: proto.Diagnostic_ERROR, 40 Summary: d.Error(), 41 }) 42 case string: 43 diags = append(diags, &proto.Diagnostic{ 44 Severity: proto.Diagnostic_WARNING, 45 Summary: d, 46 }) 47 case *proto.Diagnostic: 48 diags = append(diags, d) 49 case []*proto.Diagnostic: 50 diags = append(diags, d...) 51 } 52 return diags 53 } 54 55 // ProtoToDiagnostics converts a list of proto.Diagnostics to a tf.Diagnostics. 56 func ProtoToDiagnostics(ds []*proto.Diagnostic) tfdiags.Diagnostics { 57 var diags tfdiags.Diagnostics 58 for _, d := range ds { 59 var severity tfdiags.Severity 60 61 switch d.Severity { 62 case proto.Diagnostic_ERROR: 63 severity = tfdiags.Error 64 case proto.Diagnostic_WARNING: 65 severity = tfdiags.Warning 66 } 67 68 var newDiag tfdiags.Diagnostic 69 70 // if there's an attribute path, we need to create a AttributeValue diagnostic 71 if d.Attribute != nil { 72 path := AttributePathToPath(d.Attribute) 73 newDiag = tfdiags.AttributeValue(severity, d.Summary, d.Detail, path) 74 } else { 75 newDiag = tfdiags.WholeContainingBody(severity, d.Summary, d.Detail) 76 } 77 78 diags = diags.Append(newDiag) 79 } 80 81 return diags 82 } 83 84 // AttributePathToPath takes the proto encoded path and converts it to a cty.Path 85 func AttributePathToPath(ap *proto.AttributePath) cty.Path { 86 var p cty.Path 87 for _, step := range ap.Steps { 88 switch selector := step.Selector.(type) { 89 case *proto.AttributePath_Step_AttributeName: 90 p = p.GetAttr(selector.AttributeName) 91 case *proto.AttributePath_Step_ElementKeyString: 92 p = p.Index(cty.StringVal(selector.ElementKeyString)) 93 case *proto.AttributePath_Step_ElementKeyInt: 94 p = p.Index(cty.NumberIntVal(selector.ElementKeyInt)) 95 } 96 } 97 return p 98 } 99 100 // AttributePathToPath takes a cty.Path and converts it to a proto-encoded path. 101 func PathToAttributePath(p cty.Path) *proto.AttributePath { 102 ap := &proto.AttributePath{} 103 for _, step := range p { 104 switch selector := step.(type) { 105 case cty.GetAttrStep: 106 ap.Steps = append(ap.Steps, &proto.AttributePath_Step{ 107 Selector: &proto.AttributePath_Step_AttributeName{ 108 AttributeName: selector.Name, 109 }, 110 }) 111 case cty.IndexStep: 112 key := selector.Key 113 switch key.Type() { 114 case cty.String: 115 ap.Steps = append(ap.Steps, &proto.AttributePath_Step{ 116 Selector: &proto.AttributePath_Step_ElementKeyString{ 117 ElementKeyString: key.AsString(), 118 }, 119 }) 120 case cty.Number: 121 v, _ := key.AsBigFloat().Int64() 122 ap.Steps = append(ap.Steps, &proto.AttributePath_Step{ 123 Selector: &proto.AttributePath_Step_ElementKeyInt{ 124 ElementKeyInt: v, 125 }, 126 }) 127 default: 128 // We'll bail early if we encounter anything else, and just 129 // return the valid prefix. 130 return ap 131 } 132 } 133 } 134 return ap 135 }