Repository navigation
Expand file tree
/
Copy patherrors.go
More file actions
130 lines (118 loc) · 3.5 KB
/
Copy patherrors.go
File metadata and controls
130 lines (118 loc) · 3.5 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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package apikit
import (
"context"
"errors"
"fmt"
)
// Kind classifies an error by what a command-line tool does about it.
type Kind uint8
// The kinds of errors. The zero Kind means "no error".
const (
// KindFailed is any failure that has no more specific kind: a request
// the API refused, an unreadable file, an unexpected response.
KindFailed Kind = iota + 1
// KindUsage is a mistake on the command line: an unknown option, a
// missing argument, values that may not be combined.
KindUsage
// KindSkipped means nothing was done on purpose: a condition the caller
// set does not hold, or a result that had to be non-empty is empty.
KindSkipped
// KindNotFound means the resource asked for does not exist.
KindNotFound
// KindUnauthenticated means no usable credential is available.
KindUnauthenticated
// KindDefinition is a mistake in a definition document.
KindDefinition
// KindCanceled means the run was interrupted.
KindCanceled
)
// Exit codes a command-line tool ends with.
const (
// ExitOK is the exit code of a successful run.
ExitOK = 0
// ExitFailed is the exit code of a failed run.
ExitFailed = 1
// ExitUsage is the exit code for a mistake on the command line.
ExitUsage = 2
// ExitSkipped is the exit code when nothing was done on purpose.
ExitSkipped = 3
// ExitNotFound is the exit code when the resource does not exist.
ExitNotFound = 4
// ExitCanceled is the exit code of an interrupted run, 128 plus the
// number of the interrupt signal.
ExitCanceled = 130
)
// String returns the kind's name.
func (k Kind) String() string {
switch k {
case KindFailed:
return "failed"
case KindUsage:
return "usage"
case KindSkipped:
return "skipped"
case KindNotFound:
return "not found"
case KindUnauthenticated:
return "unauthenticated"
case KindDefinition:
return "definition"
case KindCanceled:
return "canceled"
}
return "none"
}
// ExitCode returns the exit code a command-line tool ends with for an error
// of this kind. The zero Kind maps to [ExitOK].
func (k Kind) ExitCode() int {
switch k {
case 0:
return ExitOK
case KindUsage:
return ExitUsage
case KindSkipped:
return ExitSkipped
case KindNotFound:
return ExitNotFound
case KindCanceled:
return ExitCanceled
}
return ExitFailed
}
// Error is an error with a [Kind]. Its message is complete as it stands: it
// carries no "error:" prefix and does not name the program.
type Error struct {
// Kind classifies the error.
Kind Kind
// Msg is the message shown to the user.
Msg string
// Err is the underlying error, or nil.
Err error
}
// Errorf returns an [*Error] of the given kind. An error among args that is
// wrapped with %w becomes the underlying error.
func Errorf(kind Kind, format string, args ...any) *Error {
err := fmt.Errorf(format, args...)
return &Error{Kind: kind, Msg: err.Error(), Err: errors.Unwrap(err)}
}
// Error returns the message.
func (e *Error) Error() string { return e.Msg }
// Unwrap returns the underlying error.
func (e *Error) Unwrap() error { return e.Err }
// KindOf returns the kind of err: 0 for nil, the kind of the first [*Error]
// in its chain, [KindCanceled] for a canceled context, and [KindFailed] for
// anything else.
func KindOf(err error) Kind {
if err == nil {
return 0
}
if e, ok := errors.AsType[*Error](err); ok && e.Kind != 0 {
return e.Kind
}
if errors.Is(err, context.Canceled) {
return KindCanceled
}
return KindFailed
}
// ExitCode returns the exit code for err, [ExitOK] for nil.
func ExitCode(err error) int { return KindOf(err).ExitCode() }