-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathexit.go
145 lines (124 loc) · 4.41 KB
/
exit.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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/*
Package exit defines semantic exit codes which may be used by Command Line tools
to aid in debugging and instrumentation.
This package defines exit codes in two ranges. Exit Codes 80-99 indicate a user error
of some sort. Exit Codes 100-119 indicate software or system error of some sort.
*/
package exit
import (
"errors"
"fmt"
)
// Code is the exit code that is passed to the system call `exit`
// when the program terminates. Conventionally, the value zero indicates
// success and all other values (1-255) indicate failure.
type Code = int
const (
// OK indicates that the program exited successfully.
OK Code = 0
// NotOK indicates that the program exited unsuccessfully
// but gives no extra context as to what the failure was.
NotOK Code = 1
)
// Exit Codes 80-99 are reserved for user errors.
const (
// UsageError indicates that the program exited unsuccessfully
// because it was used incorrectly.
//
// Examples: a required argument was omitted or an invalid value
// was supplied for a flag.
UsageError Code = 80
// UnknownSubcommand indicates that the program exited unsuccessfully
// because an unrecognized subcommand was invoked.
//
// This is intended for CLI multi-tools. When you run a command that
// doesn't exist from the shell, the shell exits 127. This is distinct
// from that value in that the command itself exists but the subcommand
// does not (e.g. `git nope` could exit 81).
UnknownSubcommand Code = 81
// RequirementNotMet indicates that the program exited unsuccessfully
// because a precondition wasn't satisfied.
//
// Examples: the user must be on a VPN before using the program or have
// a minimum version of some other software installed.
RequirementNotMet Code = 82
// Forbidden indicates that the program exited unsuccessfully
// because the user isn't authorized to perform the requested action.
Forbidden Code = 83
// MovedPermanently indicates that the program exited unsuccessfully
// because it has been migrated to a new location.
MovedPermanently Code = 84
)
// Exit Codes 100-119 are reserved for software or system errors.
const (
// InternalError indicates that the program exited unsuccessfully
// because of a problem in its own code.
//
// Used instead of 1 when the problem is known to be with the program's
// code or dependencies.
InternalError Code = 100
// Unavailable indicates that the program exited unsuccessfully
// because a service it depends on was not available.
//
// Examples: A local daemon or remote service did not respond, a connection
// was closed unexpectedly, an HTTP service responded with 503.
Unavailable Code = 101
)
// IsUserError reports whether an exit code is a user error.
// It returns true if the code is in the range 80-99 and false if not.
func IsUserError(code Code) bool {
return code >= 80 && code <= 99
}
// IsSoftwareError reports whether an exit code is a software error.
// It returns true if the code is in the range 100-119 and false if not.
func IsSoftwareError(code Code) bool {
return code >= 100 && code <= 119
}
// IsSignal reports whether an exit code is derived from a signal.
// It returns true if the code is in the range 128-255 and false if not.
// It also returns true if the code is -1 because ProcessState.ExitCode()
// may return -1 if the process was terminated by a signal.
func IsSignal(code Code) bool {
// -1 is not a valid exit code, but ProcessState.ExitCode()
// may return -1 if the process was terminated by a signal.
//
// https://pkg.go.dev/os#ProcessState.ExitCode
return code == -1 || code > 128 && code < 255
}
var (
ErrNotOK = Error{Code: NotOK}
ErrUsageError = Error{Code: UsageError}
ErrUnknownSubcommand = Error{Code: UnknownSubcommand}
ErrRequirementNotMet = Error{Code: RequirementNotMet}
ErrForbidden = Error{Code: Forbidden}
ErrMovedPermanently = Error{Code: MovedPermanently}
ErrInternalError = Error{Code: InternalError}
ErrUnavailable = Error{Code: Unavailable}
)
func FromError(err error) Code {
var e Error
if errors.As(err, &e) {
return e.Code
} else if err == nil {
return OK
} else {
return NotOK
}
}
func Wrap(err error, code Code) error {
return Error{Code: code, Cause: err}
}
type Error struct {
Code Code
Cause error
}
func (e Error) Error() string {
if e.Cause != nil {
return fmt.Sprintf("exit %d: %s", e.Code, e.Cause)
} else {
return fmt.Sprintf("exit %d", e.Code)
}
}
func (e Error) Unwrap() error {
return e.Cause
}