github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/tfdiags/hcl.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package tfdiags
     5  
     6  import (
     7  	"github.com/hashicorp/hcl/v2"
     8  )
     9  
    10  // hclDiagnostic is a Diagnostic implementation that wraps a HCL Diagnostic
    11  type hclDiagnostic struct {
    12  	diag *hcl.Diagnostic
    13  }
    14  
    15  var _ Diagnostic = hclDiagnostic{}
    16  
    17  func (d hclDiagnostic) Severity() Severity {
    18  	switch d.diag.Severity {
    19  	case hcl.DiagWarning:
    20  		return Warning
    21  	default:
    22  		return Error
    23  	}
    24  }
    25  
    26  func (d hclDiagnostic) Description() Description {
    27  	return Description{
    28  		Summary: d.diag.Summary,
    29  		Detail:  d.diag.Detail,
    30  	}
    31  }
    32  
    33  func (d hclDiagnostic) Source() Source {
    34  	var ret Source
    35  	if d.diag.Subject != nil {
    36  		rng := SourceRangeFromHCL(*d.diag.Subject)
    37  		ret.Subject = &rng
    38  	}
    39  	if d.diag.Context != nil {
    40  		rng := SourceRangeFromHCL(*d.diag.Context)
    41  		ret.Context = &rng
    42  	}
    43  	return ret
    44  }
    45  
    46  func (d hclDiagnostic) FromExpr() *FromExpr {
    47  	if d.diag.Expression == nil || d.diag.EvalContext == nil {
    48  		return nil
    49  	}
    50  	return &FromExpr{
    51  		Expression:  d.diag.Expression,
    52  		EvalContext: d.diag.EvalContext,
    53  	}
    54  }
    55  
    56  func (d hclDiagnostic) ExtraInfo() interface{} {
    57  	return d.diag.Extra
    58  }
    59  
    60  // SourceRangeFromHCL constructs a SourceRange from the corresponding range
    61  // type within the HCL package.
    62  func SourceRangeFromHCL(hclRange hcl.Range) SourceRange {
    63  	return SourceRange{
    64  		Filename: hclRange.Filename,
    65  		Start: SourcePos{
    66  			Line:   hclRange.Start.Line,
    67  			Column: hclRange.Start.Column,
    68  			Byte:   hclRange.Start.Byte,
    69  		},
    70  		End: SourcePos{
    71  			Line:   hclRange.End.Line,
    72  			Column: hclRange.End.Column,
    73  			Byte:   hclRange.End.Byte,
    74  		},
    75  	}
    76  }
    77  
    78  // ToHCL constructs a HCL Range from the receiving SourceRange. This is the
    79  // opposite of SourceRangeFromHCL.
    80  func (r SourceRange) ToHCL() hcl.Range {
    81  	return hcl.Range{
    82  		Filename: r.Filename,
    83  		Start: hcl.Pos{
    84  			Line:   r.Start.Line,
    85  			Column: r.Start.Column,
    86  			Byte:   r.Start.Byte,
    87  		},
    88  		End: hcl.Pos{
    89  			Line:   r.End.Line,
    90  			Column: r.End.Column,
    91  			Byte:   r.End.Byte,
    92  		},
    93  	}
    94  }
    95  
    96  // ToHCL constructs a hcl.Diagnostics containing the same diagnostic messages
    97  // as the receiving tfdiags.Diagnostics.
    98  //
    99  // This conversion preserves the data that HCL diagnostics are able to
   100  // preserve but would be lossy in a round trip from tfdiags to HCL and then
   101  // back to tfdiags, because it will lose the specific type information of
   102  // the source diagnostics. In most cases this will not be a significant
   103  // problem, but could produce an awkward result in some special cases such
   104  // as converting the result of ConsolidateWarnings, which will force the
   105  // resulting warning groups to be flattened early.
   106  func (diags Diagnostics) ToHCL() hcl.Diagnostics {
   107  	if len(diags) == 0 {
   108  		return nil
   109  	}
   110  	ret := make(hcl.Diagnostics, len(diags))
   111  	for i, diag := range diags {
   112  		severity := diag.Severity()
   113  		desc := diag.Description()
   114  		source := diag.Source()
   115  		fromExpr := diag.FromExpr()
   116  
   117  		hclDiag := &hcl.Diagnostic{
   118  			Summary:  desc.Summary,
   119  			Detail:   desc.Detail,
   120  			Severity: severity.ToHCL(),
   121  		}
   122  		if source.Subject != nil {
   123  			hclDiag.Subject = source.Subject.ToHCL().Ptr()
   124  		}
   125  		if source.Context != nil {
   126  			hclDiag.Context = source.Context.ToHCL().Ptr()
   127  		}
   128  		if fromExpr != nil {
   129  			hclDiag.Expression = fromExpr.Expression
   130  			hclDiag.EvalContext = fromExpr.EvalContext
   131  		}
   132  
   133  		ret[i] = hclDiag
   134  	}
   135  	return ret
   136  }