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  }