forked from lestrrat-go/helium
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patherrorhandler.go
More file actions
111 lines (94 loc) · 3.1 KB
/
Copy patherrorhandler.go
File metadata and controls
111 lines (94 loc) · 3.1 KB
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
package helium
import (
"context"
"sync"
"github.com/lestrrat-go/helium/sink"
)
// ErrorLeveler is an optional interface that errors can implement to
// report their severity. Errors that do not implement this interface
// are treated as warnings (ErrorLevelWarning).
type ErrorLeveler interface {
ErrorLevel() ErrorLevel
}
// ErrorHandler receives errors reported during parsing, compilation,
// or validation. Implementations may log, accumulate, or discard errors.
//
// Handle is called synchronously at the point of error detection unless
// the implementation itself introduces asynchrony (e.g. Sink[error]).
//
// Implementations must not block for extended periods.
//
// The error value may optionally implement ErrorLeveler to indicate
// severity. Users can type-assert to inspect the level.
type ErrorHandler interface {
Handle(context.Context, error)
}
// NilErrorHandler is an ErrorHandler that discards all errors.
// Use as a default when no handler is provided.
type NilErrorHandler struct{}
func (NilErrorHandler) Handle(context.Context, error) {}
type leveledError struct {
msg string
level ErrorLevel
}
func (e *leveledError) Error() string { return e.msg }
func (e *leveledError) ErrorLevel() ErrorLevel { return e.level }
// NewLeveledError creates an error that implements ErrorLeveler.
func NewLeveledError(msg string, level ErrorLevel) error {
return &leveledError{msg: msg, level: level}
}
type errorAccumulator struct {
level ErrorLevel
mu sync.Mutex
errors []error
}
func (a *errorAccumulator) Handle(_ context.Context, err error) {
if a.level != 0 {
level := ErrorLevelWarning
if l, ok := err.(ErrorLeveler); ok {
level = l.ErrorLevel()
}
if level != a.level {
return
}
}
a.mu.Lock()
a.errors = append(a.errors, err)
a.mu.Unlock()
}
func (a *errorAccumulator) collectErrors() []error {
a.mu.Lock()
defer a.mu.Unlock()
return append([]error(nil), a.errors...)
}
// ErrorCollector collects errors into a slice via an internal Sink[error].
// When level is zero (ErrorLevelNone), all errors are collected. When set,
// only errors matching that level are collected.
//
// Satisfies ErrorHandler and io.Closer. The parser/compiler closes it
// automatically at the end of the operation.
type ErrorCollector struct {
acc *errorAccumulator
s *sink.Sink[error]
}
// NewErrorCollector creates an ErrorCollector backed by a Sink[error].
// Pass ErrorLevelNone (0) for level to collect all errors regardless of severity.
func NewErrorCollector(ctx context.Context, level ErrorLevel, opts ...sink.Option) *ErrorCollector {
acc := &errorAccumulator{level: level}
return &ErrorCollector{
acc: acc,
s: sink.New[error](ctx, acc, opts...),
}
}
// Handle satisfies ErrorHandler. Sends the error to the internal Sink.
func (ec *ErrorCollector) Handle(ctx context.Context, err error) {
ec.s.Handle(ctx, err)
}
// Close satisfies io.Closer. Drains the internal Sink.
func (ec *ErrorCollector) Close() error {
return ec.s.Close()
}
// Errors returns a copy of the collected errors. Safe to call after Close.
func (ec *ErrorCollector) Errors() []error {
return ec.acc.collectErrors()
}