Skip to content

Commit dd32086

Browse files
committed
x 初稿
1 parent b648d8b commit dd32086

7 files changed

Lines changed: 453 additions & 1 deletion

File tree

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module github.com/FishGoddess/errors
22

3-
go 1.17
3+
go 1.20

x/error.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright 2024 FishGoddess. All rights reserved.
2+
// Use of this source code is governed by a MIT style
3+
// license that can be found in the LICENSE file.
4+
5+
package x
6+
7+
import (
8+
"fmt"
9+
)
10+
11+
type Error struct {
12+
code int32
13+
message string
14+
cause error
15+
}
16+
17+
// New returns *Error with code and message.
18+
func New(code int32, message string) *Error {
19+
return &Error{
20+
code: code,
21+
message: message,
22+
}
23+
}
24+
25+
// Newf returns *Error with code and formatted message.
26+
func Newf(code int32, format string, args ...interface{}) *Error {
27+
message := fmt.Sprintf(format, args...)
28+
return New(code, message)
29+
}
30+
31+
// Wrap wraps err to *Error with code and message.
32+
func Wrap(err error, code int32, message string) *Error {
33+
return &Error{
34+
code: code,
35+
message: message,
36+
cause: err,
37+
}
38+
}
39+
40+
// Wrapf wraps err to *Error with code and formatted message.
41+
func Wrapf(err error, code int32, format string, args ...interface{}) *Error {
42+
message := fmt.Sprintf(format, args...)
43+
return Wrap(err, code, message)
44+
}
45+
46+
// Code returns the code of *Error.
47+
func (e *Error) Code() int32 {
48+
return e.code
49+
}
50+
51+
// Message returns the message of *Error.
52+
func (e *Error) Message() string {
53+
return e.message
54+
}
55+
56+
// Unwrap unwraps *Error and returns its cause error.
57+
func (e *Error) Unwrap() error {
58+
return e.cause
59+
}
60+
61+
// Error returns *Error as string.
62+
func (e *Error) Error() string {
63+
return e.String()
64+
}
65+
66+
// String returns *Error as string.
67+
func (e *Error) String() string {
68+
if e.cause == nil {
69+
return fmt.Sprintf("%d: %s", e.code, e.message)
70+
}
71+
72+
return fmt.Sprintf("%d: %s (%+v)", e.code, e.message, e.cause)
73+
}

x/error_test.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Copyright 2024 FishGoddess. All rights reserved.
2+
// Use of this source code is governed by a MIT style
3+
// license that can be found in the LICENSE file.
4+
5+
package x
6+
7+
import (
8+
"io"
9+
"testing"
10+
)
11+
12+
// go test -v -cover -count=1 -test.cpu=1 -run=^TestNew$
13+
func TestNew(t *testing.T) {
14+
testCases := []struct {
15+
code int32
16+
message string
17+
}{
18+
{code: -1000, message: "io timeout"},
19+
{code: 1000, message: "need login"},
20+
}
21+
22+
for _, testCase := range testCases {
23+
err := New(testCase.code, testCase.message)
24+
if err.Code() != testCase.code {
25+
t.Errorf("err.Code() %d != testCase.code %d", err.Code(), testCase.code)
26+
}
27+
28+
if err.Message() != testCase.message {
29+
t.Errorf("err.Message() %s != testCase.code %s", err.Message(), testCase.message)
30+
}
31+
32+
if err.Unwrap() != nil {
33+
t.Errorf("err.Unwrap() %+v != nil", err.Unwrap())
34+
}
35+
}
36+
}
37+
38+
// go test -v -cover -count=1 -test.cpu=1 -run=^TestWrap$
39+
func TestWrap(t *testing.T) {
40+
testCases := []struct {
41+
code int32
42+
message string
43+
cause error
44+
}{
45+
{code: -1000, message: "eof", cause: io.EOF},
46+
{code: 1000, message: "need login"},
47+
}
48+
49+
for _, testCase := range testCases {
50+
err := Wrap(testCase.cause, testCase.code, testCase.message)
51+
if err.Code() != testCase.code {
52+
t.Errorf("err.Code() %d != testCase.code %d", err.Code(), testCase.code)
53+
}
54+
55+
if err.Message() != testCase.message {
56+
t.Errorf("err.Message() %s != testCase.code %s", err.Message(), testCase.message)
57+
}
58+
59+
if err.Unwrap() != testCase.cause {
60+
t.Errorf("err.Unwrap() %+v != testCase.code %+v", err.Unwrap(), testCase.cause)
61+
}
62+
}
63+
}
64+
65+
// go test -v -cover -count=1 -test.cpu=1 -run=^TestErrorError$
66+
func TestErrorError(t *testing.T) {
67+
testCases := []struct {
68+
code int32
69+
message string
70+
cause error
71+
errorString string
72+
}{
73+
{code: -1000, message: "eof", cause: io.EOF, errorString: "-1000: eof (EOF)"},
74+
{code: 1000, message: "need login", errorString: "1000: need login"},
75+
}
76+
77+
for _, testCase := range testCases {
78+
err := Wrap(testCase.cause, testCase.code, testCase.message)
79+
if err.Error() != testCase.errorString {
80+
t.Errorf("err.Error() %s != testCase.errorString %s", err.Error(), testCase.errorString)
81+
}
82+
}
83+
}

x/shortcut.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2024 FishGoddess. All rights reserved.
2+
// Use of this source code is governed by a MIT style
3+
// license that can be found in the LICENSE file.
4+
5+
package x
6+
7+
import (
8+
"errors"
9+
)
10+
11+
// Is is a shortcut of errors.Is.
12+
func Is(err, target error) bool {
13+
return errors.Is(err, target)
14+
}
15+
16+
// As is a shortcut of errors.As.
17+
func As(err error, target any) bool {
18+
return errors.As(err, target)
19+
}
20+
21+
// Unwrap is a shortcut of errors.Unwrap.
22+
func Unwrap(err error) error {
23+
return errors.Unwrap(err)
24+
}
25+
26+
// Join is a shortcut of errors.Join.
27+
func Join(errs ...error) error {
28+
return errors.Join(errs...)
29+
}

x/shortcut_test.go

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
// Copyright 2024 FishGoddess. All rights reserved.
2+
// Use of this source code is governed by a MIT style
3+
// license that can be found in the LICENSE file.
4+
5+
package x
6+
7+
import (
8+
"os"
9+
"testing"
10+
)
11+
12+
type testError struct {
13+
reason string
14+
}
15+
16+
func (te *testError) Code() int32 {
17+
return 500
18+
}
19+
20+
func (te *testError) Error() string {
21+
return te.reason
22+
}
23+
24+
// go test -v -cover -count=1 -test.cpu=1 -run=^TestIs$
25+
func TestIs(t *testing.T) {
26+
testErr := &testError{reason: "test error"}
27+
wrapErr := Wrap(testErr, -1000, "wrap test error")
28+
29+
testCases := []struct {
30+
err error
31+
cause error
32+
}{
33+
{
34+
err: Wrap(testErr, 1000, "wow"),
35+
cause: testErr,
36+
},
37+
{
38+
err: Wrap(wrapErr, 1000, "wow too"),
39+
cause: testErr,
40+
},
41+
}
42+
43+
for _, testCase := range testCases {
44+
if ok := Is(testCase.err, testCase.cause); !ok {
45+
t.Errorf("testCase.err %+v isn't testCase.cause %+v", testCase.err, testCase.cause)
46+
}
47+
}
48+
}
49+
50+
// go test -v -cover -count=1 -test.cpu=1 -run=^TestAs$
51+
func TestAs(t *testing.T) {
52+
testErr := &testError{reason: "test error"}
53+
wrapErr := Wrap(testErr, -1000, "wrap test error")
54+
55+
targetTestErr := &testError{}
56+
targetTestErr2 := &testError{}
57+
targetPathErr := &os.PathError{}
58+
59+
testCases := []struct {
60+
err error
61+
target any
62+
ok bool
63+
want any
64+
}{
65+
{
66+
err: Wrap(testErr, 1000, "wow"),
67+
target: &targetTestErr,
68+
ok: true,
69+
want: &testErr,
70+
},
71+
{
72+
err: Wrap(wrapErr, 1000, "wow too"),
73+
target: &targetTestErr2,
74+
ok: true,
75+
want: &testErr,
76+
},
77+
{
78+
err: New(1000, "no"),
79+
target: &targetPathErr,
80+
ok: false,
81+
want: nil,
82+
},
83+
}
84+
85+
for _, testCase := range testCases {
86+
ok := As(testCase.err, testCase.target)
87+
if ok != testCase.ok {
88+
t.Errorf("err %+v ok %+v != err %+v testCase.ok %+v", testCase.err, testCase.target, ok, testCase.ok)
89+
}
90+
}
91+
}
92+
93+
// go test -v -cover -count=1 -test.cpu=1 -run=^TestUnwrap$
94+
func TestUnwrap(t *testing.T) {
95+
testErr := &testError{reason: "test error"}
96+
wrapErr := Wrap(testErr, -1000, "wrap test error")
97+
98+
testCases := []struct {
99+
err error
100+
cause error
101+
}{
102+
{
103+
err: testErr,
104+
cause: nil,
105+
},
106+
{
107+
err: Wrap(testErr, 1000, "wow"),
108+
cause: testErr,
109+
},
110+
{
111+
err: Wrap(wrapErr, 1000, "wow too"),
112+
cause: wrapErr,
113+
},
114+
}
115+
116+
for _, testCase := range testCases {
117+
if Unwrap(testCase.err) != testCase.cause {
118+
t.Errorf("Unwrap(testCase.err) %+v != testCase.cause %+v", Unwrap(testCase.err), testCase.cause)
119+
}
120+
}
121+
}

x/unwrap.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright 2024 FishGoddess. All rights reserved.
2+
// Use of this source code is governed by a MIT style
3+
// license that can be found in the LICENSE file.
4+
5+
package x
6+
7+
// Code unwraps error and gets code of it.
8+
// It returns 0 if err is nil.
9+
// It returns defaultCode if err doesn't have a code.
10+
func Code(err error, defaultCode int32) int32 {
11+
if err == nil {
12+
return 0
13+
}
14+
15+
cerr, ok := err.(interface {
16+
Code() int32
17+
})
18+
if ok {
19+
return cerr.Code()
20+
}
21+
22+
var xerr *Error
23+
if As(err, &xerr) {
24+
return xerr.Code()
25+
}
26+
27+
return defaultCode
28+
}
29+
30+
// Message unwraps error and gets message of it.
31+
// It returns "" if err is nil.
32+
// It returns defaultMessage if err doesn't have a message.
33+
func Message(err error, defaultMessage string) string {
34+
if err == nil {
35+
return ""
36+
}
37+
38+
merr, ok := err.(interface {
39+
Message() string
40+
})
41+
if ok {
42+
return merr.Message()
43+
}
44+
45+
var xerr *Error
46+
if As(err, &xerr) {
47+
return xerr.Message()
48+
}
49+
50+
return defaultMessage
51+
}
52+
53+
// CodeMessage unwraps error and gets code & message of it.
54+
// It returns 0 & "" if err is nil.
55+
// It returns defaultCode if err doesn't have a code.
56+
// It returns defaultMessage if err doesn't have a message.
57+
func CodeMessage(err error, defaultCode int32, defaultMessage string) (int32, string) {
58+
code := Code(err, defaultCode)
59+
message := Message(err, defaultMessage)
60+
61+
return code, message
62+
}

0 commit comments

Comments
 (0)