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