Skip to content

Commit bfd65ae

Browse files
committed
Init commit
1 parent 519f646 commit bfd65ae

4 files changed

Lines changed: 126 additions & 0 deletions

File tree

code.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package exitcode
2+
3+
import (
4+
"flag"
5+
"os"
6+
7+
"golang.org/x/xerrors"
8+
)
9+
10+
// Coder is an interface to control what value Get returns.
11+
type Coder interface {
12+
error
13+
ExitCode() int
14+
}
15+
16+
// Get gets the exit code associated with an error. Cases:
17+
//
18+
// nil => 0
19+
// errors implementing Coder => value returned by ExitCode
20+
// flag.ErrHelp => 2
21+
// all other errors => 1
22+
func Get(err error) int {
23+
if err == nil {
24+
return 0
25+
}
26+
27+
if coder := Coder(nil); xerrors.As(err, &coder) {
28+
return coder.ExitCode()
29+
}
30+
31+
if xerrors.Is(err, flag.ErrHelp) {
32+
return 2
33+
}
34+
35+
return 1
36+
}
37+
38+
// Set wraps an error in a Coder, setting its error code.
39+
func Set(err error, code int) error {
40+
return coder{err, code}
41+
}
42+
43+
var _ Coder = coder{}
44+
45+
type coder struct {
46+
error
47+
int
48+
}
49+
50+
func (co coder) ExitCode() int {
51+
return co.int
52+
}
53+
54+
func (co coder) Unwrap() error {
55+
return co.error
56+
}
57+
58+
// Exit is a convenience function that calls os.Exit
59+
// with the exit code associated with err.
60+
func Exit(err error) {
61+
os.Exit(Get(err))
62+
}

code_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package exitcode_test
2+
3+
import (
4+
"errors"
5+
"flag"
6+
"testing"
7+
8+
"github.com/carlmjohnson/exitcode"
9+
"golang.org/x/xerrors"
10+
)
11+
12+
func TestGet(t *testing.T) {
13+
base := exitcode.Set(errors.New(""), 4)
14+
wrapped := xerrors.Errorf("wrapping: %w", base)
15+
16+
testCases := map[string]struct {
17+
error
18+
int
19+
}{
20+
"nil": {nil, 0},
21+
"default": {errors.New(""), 1},
22+
"help": {flag.ErrHelp, 2},
23+
"set": {exitcode.Set(errors.New(""), 3), 3},
24+
"wrapped": {wrapped, 4},
25+
}
26+
27+
for name, tc := range testCases {
28+
t.Run(name, func(t *testing.T) {
29+
err := tc.error
30+
want := tc.int
31+
got := exitcode.Get(err)
32+
if got != want {
33+
t.Errorf("%v: %d != %d", err, got, want)
34+
}
35+
})
36+
}
37+
}
38+
39+
func TestSet(t *testing.T) {
40+
t.Run("same-message", func(t *testing.T) {
41+
err := xerrors.New("hello")
42+
coder := exitcode.Set(err, 2)
43+
got := err.Error()
44+
want := coder.Error()
45+
if got != want {
46+
t.Errorf("error message %q != %q", got, want)
47+
}
48+
})
49+
t.Run("keep-chain", func(t *testing.T) {
50+
err := xerrors.New("hello")
51+
coder := exitcode.Set(err, 3)
52+
53+
if !xerrors.Is(coder, err) {
54+
t.Errorf("broken chain: %v is not %v", coder, err)
55+
}
56+
})
57+
}

go.mod

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module github.com/carlmjohnson/exitcode
2+
3+
go 1.12
4+
5+
require golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522 h1:bhOzK9QyoD0ogCnFro1m2mz41+Ib0oOhfJnBp5MR4K4=
2+
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

0 commit comments

Comments
 (0)