aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/terraform/tfdiags/diagnostics.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/tfdiags/diagnostics.go')
-rw-r--r--vendor/github.com/hashicorp/terraform/tfdiags/diagnostics.go149
1 files changed, 149 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/diagnostics.go b/vendor/github.com/hashicorp/terraform/tfdiags/diagnostics.go
index 667ba80..465b230 100644
--- a/vendor/github.com/hashicorp/terraform/tfdiags/diagnostics.go
+++ b/vendor/github.com/hashicorp/terraform/tfdiags/diagnostics.go
@@ -3,6 +3,9 @@ package tfdiags
3import ( 3import (
4 "bytes" 4 "bytes"
5 "fmt" 5 "fmt"
6 "path/filepath"
7 "sort"
8 "strings"
6 9
7 "github.com/hashicorp/errwrap" 10 "github.com/hashicorp/errwrap"
8 multierror "github.com/hashicorp/go-multierror" 11 multierror "github.com/hashicorp/go-multierror"
@@ -54,6 +57,8 @@ func (diags Diagnostics) Append(new ...interface{}) Diagnostics {
54 diags = append(diags, ti...) // flatten 57 diags = append(diags, ti...) // flatten
55 case diagnosticsAsError: 58 case diagnosticsAsError:
56 diags = diags.Append(ti.Diagnostics) // unwrap 59 diags = diags.Append(ti.Diagnostics) // unwrap
60 case NonFatalError:
61 diags = diags.Append(ti.Diagnostics) // unwrap
57 case hcl.Diagnostics: 62 case hcl.Diagnostics:
58 for _, hclDiag := range ti { 63 for _, hclDiag := range ti {
59 diags = append(diags, hclDiagnostic{hclDiag}) 64 diags = append(diags, hclDiagnostic{hclDiag})
@@ -136,6 +141,54 @@ func (diags Diagnostics) Err() error {
136 return diagnosticsAsError{diags} 141 return diagnosticsAsError{diags}
137} 142}
138 143
144// ErrWithWarnings is similar to Err except that it will also return a non-nil
145// error if the receiver contains only warnings.
146//
147// In the warnings-only situation, the result is guaranteed to be of dynamic
148// type NonFatalError, allowing diagnostics-aware callers to type-assert
149// and unwrap it, treating it as non-fatal.
150//
151// This should be used only in contexts where the caller is able to recognize
152// and handle NonFatalError. For normal callers that expect a lack of errors
153// to be signaled by nil, use just Diagnostics.Err.
154func (diags Diagnostics) ErrWithWarnings() error {
155 if len(diags) == 0 {
156 return nil
157 }
158 if diags.HasErrors() {
159 return diags.Err()
160 }
161 return NonFatalError{diags}
162}
163
164// NonFatalErr is similar to Err except that it always returns either nil
165// (if there are no diagnostics at all) or NonFatalError.
166//
167// This allows diagnostics to be returned over an error return channel while
168// being explicit that the diagnostics should not halt processing.
169//
170// This should be used only in contexts where the caller is able to recognize
171// and handle NonFatalError. For normal callers that expect a lack of errors
172// to be signaled by nil, use just Diagnostics.Err.
173func (diags Diagnostics) NonFatalErr() error {
174 if len(diags) == 0 {
175 return nil
176 }
177 return NonFatalError{diags}
178}
179
180// Sort applies an ordering to the diagnostics in the receiver in-place.
181//
182// The ordering is: warnings before errors, sourceless before sourced,
183// short source paths before long source paths, and then ordering by
184// position within each file.
185//
186// Diagnostics that do not differ by any of these sortable characteristics
187// will remain in the same relative order after this method returns.
188func (diags Diagnostics) Sort() {
189 sort.Stable(sortDiagnostics(diags))
190}
191
139type diagnosticsAsError struct { 192type diagnosticsAsError struct {
140 Diagnostics 193 Diagnostics
141} 194}
@@ -179,3 +232,99 @@ func (dae diagnosticsAsError) WrappedErrors() []error {
179 } 232 }
180 return errs 233 return errs
181} 234}
235
236// NonFatalError is a special error type, returned by
237// Diagnostics.ErrWithWarnings and Diagnostics.NonFatalErr,
238// that indicates that the wrapped diagnostics should be treated as non-fatal.
239// Callers can conditionally type-assert an error to this type in order to
240// detect the non-fatal scenario and handle it in a different way.
241type NonFatalError struct {
242 Diagnostics
243}
244
245func (woe NonFatalError) Error() string {
246 diags := woe.Diagnostics
247 switch {
248 case len(diags) == 0:
249 // should never happen, since we don't create this wrapper if
250 // there are no diagnostics in the list.
251 return "no errors or warnings"
252 case len(diags) == 1:
253 desc := diags[0].Description()
254 if desc.Detail == "" {
255 return desc.Summary
256 }
257 return fmt.Sprintf("%s: %s", desc.Summary, desc.Detail)
258 default:
259 var ret bytes.Buffer
260 if diags.HasErrors() {
261 fmt.Fprintf(&ret, "%d problems:\n", len(diags))
262 } else {
263 fmt.Fprintf(&ret, "%d warnings:\n", len(diags))
264 }
265 for _, diag := range woe.Diagnostics {
266 desc := diag.Description()
267 if desc.Detail == "" {
268 fmt.Fprintf(&ret, "\n- %s", desc.Summary)
269 } else {
270 fmt.Fprintf(&ret, "\n- %s: %s", desc.Summary, desc.Detail)
271 }
272 }
273 return ret.String()
274 }
275}
276
277// sortDiagnostics is an implementation of sort.Interface
278type sortDiagnostics []Diagnostic
279
280var _ sort.Interface = sortDiagnostics(nil)
281
282func (sd sortDiagnostics) Len() int {
283 return len(sd)
284}
285
286func (sd sortDiagnostics) Less(i, j int) bool {
287 iD, jD := sd[i], sd[j]
288 iSev, jSev := iD.Severity(), jD.Severity()
289 iSrc, jSrc := iD.Source(), jD.Source()
290
291 switch {
292
293 case iSev != jSev:
294 return iSev == Warning
295
296 case (iSrc.Subject == nil) != (jSrc.Subject == nil):
297 return iSrc.Subject == nil
298
299 case iSrc.Subject != nil && *iSrc.Subject != *jSrc.Subject:
300 iSubj := iSrc.Subject
301 jSubj := jSrc.Subject
302 switch {
303 case iSubj.Filename != jSubj.Filename:
304 // Path with fewer segments goes first if they are different lengths
305 sep := string(filepath.Separator)
306 iCount := strings.Count(iSubj.Filename, sep)
307 jCount := strings.Count(jSubj.Filename, sep)
308 if iCount != jCount {
309 return iCount < jCount
310 }
311 return iSubj.Filename < jSubj.Filename
312 case iSubj.Start.Byte != jSubj.Start.Byte:
313 return iSubj.Start.Byte < jSubj.Start.Byte
314 case iSubj.End.Byte != jSubj.End.Byte:
315 return iSubj.End.Byte < jSubj.End.Byte
316 }
317 fallthrough
318
319 default:
320 // The remaining properties do not have a defined ordering, so
321 // we'll leave it unspecified. Since we use sort.Stable in
322 // the caller of this, the ordering of remaining items will
323 // be preserved.
324 return false
325 }
326}
327
328func (sd sortDiagnostics) Swap(i, j int) {
329 sd[i], sd[j] = sd[j], sd[i]
330}