aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/terraform-config-inspect/tfconfig/diagnostic.go
blob: 8d04ad4de2b7a7aba7a3a6b6b95fa7563d420b17 (plain) (blame)
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
package tfconfig

import (
	"fmt"

	legacyhclparser "github.com/hashicorp/hcl/hcl/parser"
	"github.com/hashicorp/hcl2/hcl"
)

// 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...))
}