1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
|
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package tfconfig
import (
"fmt"
legacyhclparser "github.com/hashicorp/hcl/hcl/parser"
"github.com/hashicorp/hcl/v2"
)
// Diagnostic describes a problem (error or warning) encountered during
// configuration loading.
type Diagnostic struct {
Severity DiagSeverity `json:"severity"`
Summary string `json:"summary"`
Detail string `json:"detail,omitempty"`
// Pos is not populated for all diagnostics, but when populated should
// indicate a particular line that the described problem relates to.
Pos *SourcePos `json:"pos,omitempty"`
}
// Diagnostics represents a sequence of diagnostics. This is the type that
// should be returned from a function that might generate diagnostics.
type Diagnostics []Diagnostic
// HasErrors returns true if there is at least one Diagnostic of severity
// DiagError in the receiever.
//
// If a function returns a Diagnostics without errors then the result can
// be assumed to be complete within the "best effort" constraints of this
// library. If errors are present then the caller may wish to employ more
// caution in relying on the result.
func (diags Diagnostics) HasErrors() bool {
for _, diag := range diags {
if diag.Severity == DiagError {
return true
}
}
return false
}
func (diags Diagnostics) Error() string {
switch len(diags) {
case 0:
return "no problems"
case 1:
return fmt.Sprintf("%s: %s", diags[0].Summary, diags[0].Detail)
default:
return fmt.Sprintf("%s: %s (and %d other messages)", diags[0].Summary, diags[0].Detail, len(diags)-1)
}
}
// Err returns an error representing the receiver if the receiver HasErrors, or
// nil otherwise.
//
// The returned error can be type-asserted back to a Diagnostics if needed.
func (diags Diagnostics) Err() error {
if diags.HasErrors() {
return diags
}
return nil
}
// DiagSeverity describes the severity of a Diagnostic.
type DiagSeverity rune
// DiagError indicates a problem that prevented proper processing of the
// configuration. In the precense of DiagError diagnostics the result is
// likely to be incomplete.
const DiagError DiagSeverity = 'E'
// DiagWarning indicates a problem that the user may wish to consider but
// that did not prevent proper processing of the configuration.
const DiagWarning DiagSeverity = 'W'
// MarshalJSON is an implementation of encoding/json.Marshaler
func (s DiagSeverity) MarshalJSON() ([]byte, error) {
switch s {
case DiagError:
return []byte(`"error"`), nil
case DiagWarning:
return []byte(`"warning"`), nil
default:
return []byte(`"invalid"`), nil
}
}
func diagnosticsHCL(diags hcl.Diagnostics) Diagnostics {
if len(diags) == 0 {
return nil
}
ret := make(Diagnostics, len(diags))
for i, diag := range diags {
ret[i] = Diagnostic{
Summary: diag.Summary,
Detail: diag.Detail,
}
switch diag.Severity {
case hcl.DiagError:
ret[i].Severity = DiagError
case hcl.DiagWarning:
ret[i].Severity = DiagWarning
}
if diag.Subject != nil {
pos := sourcePosHCL(*diag.Subject)
ret[i].Pos = &pos
}
}
return ret
}
func diagnosticsError(err error) Diagnostics {
if err == nil {
return nil
}
if posErr, ok := err.(*legacyhclparser.PosError); ok {
pos := sourcePosLegacyHCL(posErr.Pos, "")
return Diagnostics{
Diagnostic{
Severity: DiagError,
Summary: posErr.Err.Error(),
Pos: &pos,
},
}
}
return Diagnostics{
Diagnostic{
Severity: DiagError,
Summary: err.Error(),
},
}
}
func diagnosticsErrorf(format string, args ...interface{}) Diagnostics {
return diagnosticsError(fmt.Errorf(format, args...))
}
|