-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathoptions.go
More file actions
95 lines (85 loc) · 4.46 KB
/
options.go
File metadata and controls
95 lines (85 loc) · 4.46 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
package errors
import "log/slog"
// This file holds the orthogonal-axis [Option]s that extend the core
// (message + severity + code + cause) without touching it. Each axis is an
// independent harvestable layer (cockroachdb/errors' insight): provenance
// (WithStack, in stack.go), structured attrs (WithAttr/WithAttrs), operator
// guidance (WithHint), the public/internal split (WithPublic/MarkSecret),
// process-exit mapping (WithExitCode), and retry behaviour (WithTemporary/
// WithRetryable). All are stdlib-only and invisible in [Error.Error].
// WithAttr attaches a single structured key/value pair to the error being
// built, recorded as a [slog.Attr] so it bridges to a structured logger
// verbatim (see [Error.LogValue]). Prefer this over folding values into the
// message: an attr is searchable and machine-readable, a stringly-formatted
// message is neither.
//
// return errs.Wrap(err, "fetch tenant", errs.WithAttr(slog.String("tenant", id)))
func WithAttr(a slog.Attr) Option {
return func(e *Error) { e.attrs = append(e.attrs, a) }
}
// WithAttrs attaches several structured pairs at once, the bulk form of
// [WithAttr]. Order is preserved and appends to any attrs already set.
func WithAttrs(attrs ...slog.Attr) Option {
return func(e *Error) { e.attrs = append(e.attrs, attrs...) }
}
// WithHint attaches operator-facing remediation guidance — what a human should
// DO about the failure, as opposed to what went wrong. Hints are invisible in
// [Error.Error] and surface only through [HintsOf] and the slog bridge, so a
// hint never pollutes the error message but is always available to a
// diagnostics renderer.
//
// errs.New("config not found", errs.WithHint("run `tool init` to scaffold one"))
//
// Multiple hints accumulate across a chain; [HintsOf] returns them all.
func WithHint(hint string) Option {
return func(e *Error) { e.hints = append(e.hints, hint) }
}
// WithPublic sets the safe-to-surface message: the redacted, end-user-facing
// summary that omits internal detail. The operator-only message and cause
// chain remain in [Error.Error]; the public message is read separately via
// [PublicOf]. This is the explicit Public-vs-internal split (samber/oops): one
// Error value drives both the verbose operator log and the terse user reply.
//
// errs.Wrap(dbErr, "query user by email",
// errs.WithPublic("something went wrong, please try again"))
//
// The outermost (most context-rich) public message wins in [PublicOf].
func WithPublic(msg string) Option {
return func(e *Error) { e.public = msg }
}
// MarkSecret records that this error carries sensitive data and supplies the
// safe public summary to surface in its place. It is the redaction-by-default
// posture expressed as an option: where [WithPublic] adds a friendly summary,
// MarkSecret asserts that the internal message/attrs must not escape and gives
// the only string that may.
//
// MarkSecret is shorthand for [WithPublic] with the additional semantic intent
// of redaction; the public message it sets is the value [PublicOf] returns.
func MarkSecret(publicSummary string) Option {
return func(e *Error) { e.public = publicSummary }
}
// WithExitCode maps this error layer to a process exit code, consumed by
// [ExitCodeOf] / [Exit]. Use the [sysexits.h]-derived constants (see exit.go)
// rather than bare integers so the vocabulary stays uniform across the fleet.
// Exit code 0 is a legitimate explicit choice (it is how cli-go/borealis map
// `ErrHelp` — a clean, requested early exit), which is why presence is tracked
// independently of the value.
//
// var ErrUsage = errs.New("bad flags", errs.WithExitCode(errs.ExitUsage))
func WithExitCode(code int) Option {
return func(e *Error) { e.exitCode, e.hasExit = code, true }
}
// WithTemporary opts this error into the [IsTemporary] behaviour carrier (the
// net.Error / Cheney convention): temporary == the operation may succeed if
// retried after a transient condition clears. Classify by behaviour, never by
// sentinel comparison.
func WithTemporary(temporary bool) Option {
return func(e *Error) { e.temporary, e.hasTemporary = temporary, true }
}
// WithRetryable opts this error into the [IsRetryable] behaviour carrier:
// retryable == a caller's retry policy is permitted to re-attempt. Distinct
// from temporary (a transient-condition signal) so a caller can express "do
// not retry this even though it looks transient" and vice versa.
func WithRetryable(retryable bool) Option {
return func(e *Error) { e.retryable, e.hasRetryable = retryable, true }
}