-
Notifications
You must be signed in to change notification settings - Fork 478
/
error.go
106 lines (93 loc) · 2.63 KB
/
error.go
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
package vm
import (
"fmt"
"strings"
"github.com/grafana/agent/pkg/river/ast"
"github.com/grafana/agent/pkg/river/diag"
"github.com/grafana/agent/pkg/river/internal/value"
"github.com/grafana/agent/pkg/river/printer"
"github.com/grafana/agent/pkg/river/token/builder"
)
// makeDiagnostic tries to convert err into a diag.Diagnostic. err must be an
// error from the river/internal/value package, otherwise err will be returned
// unmodified.
func makeDiagnostic(err error, assoc map[value.Value]ast.Node) error {
var (
node ast.Node
expr strings.Builder
message string
cause value.Value
// Until we find a node, we're not a literal error.
literal = false
)
isValueError := value.WalkError(err, func(err error) {
var val value.Value
switch ne := err.(type) {
case value.Error:
message = ne.Error()
val = ne.Value
case value.TypeError:
message = fmt.Sprintf("should be %s, got %s", ne.Expected, ne.Value.Type())
val = ne.Value
case value.MissingKeyError:
message = fmt.Sprintf("does not have field named %q", ne.Missing)
val = ne.Value
case value.ElementError:
fmt.Fprintf(&expr, "[%d]", ne.Index)
val = ne.Value
case value.FieldError:
fmt.Fprintf(&expr, ".%s", ne.Field)
val = ne.Value
}
cause = val
if foundNode, ok := assoc[val]; ok {
// If we just found a direct node, we can reset the expression buffer so
// we don't unnecessarily print element and field accesses for we can see
// directly in the file.
if literal {
expr.Reset()
}
node = foundNode
literal = true
} else {
literal = false
}
})
if !isValueError {
return err
}
if node != nil {
var nodeText strings.Builder
if err := printer.Fprint(&nodeText, node); err != nil {
// This should never panic; printer.Fprint only fails when given an
// unexpected type, which we never do here.
panic(err)
}
// Merge the node text with the expression together (which will be relative
// accesses to the expression).
message = fmt.Sprintf("%s%s %s", nodeText.String(), expr.String(), message)
} else {
message = fmt.Sprintf("%s %s", expr.String(), message)
}
// Render the underlying problematic value as a string.
var valueText string
if cause != value.Null {
be := builder.NewExpr()
be.SetValue(cause.Interface())
valueText = string(be.Bytes())
}
if literal {
// Hide the value if the node itself has the error we were worried about.
valueText = ""
}
d := diag.Diagnostic{
Severity: diag.SeverityLevelError,
Message: message,
Value: valueText,
}
if node != nil {
d.StartPos = ast.StartPos(node).Position()
d.EndPos = ast.EndPos(node).Position()
}
return d
}