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