github.com/opentofu/opentofu@v1.7.1/internal/plugin6/convert/diagnostics.go (about)

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