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  }