Skip to content

Commit a9adc53

Browse files
committed
add errno
1 parent d14358c commit a9adc53

File tree

8 files changed

+1430
-0
lines changed

8 files changed

+1430
-0
lines changed

errno/convert.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// MIT License
2+
// Copyright (c) 2020 Qi Yin <[email protected]>
3+
4+
package errno
5+
6+
import (
7+
"errors"
8+
)
9+
10+
var (
11+
// InternalServerError 系统错误,非 Errno 实现均被 Parse 解析为系统错误
12+
InternalServerError = &errno{code: -1, message: "System Error"}
13+
14+
// OK error 为nil 时会被 Parse 解析为 OK
15+
OK = &errno{code: 0, message: "ok"}
16+
)
17+
18+
// To 把任意 error 转成 Errno 类型
19+
// 如果 err 实现了 Errno 接口,则返回 err 本身
20+
// 如果 err 为 nil, 返回预定义 OK, 默认 code=0, message=ok
21+
// 如果 err 没有实现 Errno,返回预定义 InternalServerError,并使用 WrapErrno 包装 err,默认 code=-1, message=System Error
22+
func To(err error) Errno {
23+
if err == nil {
24+
return OK
25+
}
26+
27+
switch e := err.(type) {
28+
case Errno:
29+
return e
30+
}
31+
32+
return Wrap(InternalServerError).Err(err)
33+
}
34+
35+
// Parse 返回错误码、错误消息以及包装错误
36+
// Parse 首先使用 To(err) 获得一个 Errno 实现
37+
// 包装错误使用 errors.Unwrap 解析获得
38+
func Parse(err error) (int, string, error) {
39+
w := To(err)
40+
return w.Code(), w.Message(), errors.Unwrap(w)
41+
}

errno/convert_test.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package errno
2+
3+
import (
4+
"bytes"
5+
"errors"
6+
"os"
7+
"testing"
8+
)
9+
10+
func TestTo(t *testing.T) {
11+
systemErr := errors.New("system error")
12+
wrapE := errors.New("wrap error")
13+
for _, v := range []struct {
14+
err error
15+
code int
16+
message string
17+
wrapErr error
18+
}{
19+
{systemErr, -1, "System Error", systemErr},
20+
{systemErr, -1, "System Error", InternalServerError},
21+
{os.ErrNotExist, -1, "System Error", os.ErrNotExist},
22+
{bytes.ErrTooLarge, -1, "System Error", bytes.ErrTooLarge},
23+
{nil, 0, "ok", OK},
24+
{New(10, "testing"), 10, "testing", nil},
25+
{NewCode(20), 20, "", nil},
26+
{Wrap(New(10, "testing")).Comment("error comment"), 10, "testing: error comment", nil},
27+
{Wrap(New(10, "testing")).Err(wrapE), 10, "testing", wrapE},
28+
{Wrap(New(10, "testing %s")).Err(wrapE).Format("format"), 10, "testing format", wrapE},
29+
} {
30+
err := To(v.err)
31+
if err.Code() != v.code {
32+
t.Errorf("To(v.err).Code():%d != v.code:%d", err.Code(), v.code)
33+
}
34+
35+
if err.Message() != v.message {
36+
t.Errorf("To(v.err).Message():%s != v.message:%s", err.Message(), v.message)
37+
}
38+
39+
if v.wrapErr != nil && !errors.Is(err, v.wrapErr) {
40+
t.Errorf("errors.Is(err, v.wrapErr:%v) != true", v.wrapErr)
41+
}
42+
}
43+
}
44+
45+
func TestParse(t *testing.T) {
46+
systemErr := errors.New("system error")
47+
wrapE := errors.New("wrap error")
48+
for _, v := range []struct {
49+
err error
50+
code int
51+
message string
52+
wrapErr error
53+
}{
54+
{systemErr, -1, "System Error", systemErr},
55+
{systemErr, -1, "System Error", InternalServerError},
56+
{os.ErrNotExist, -1, "System Error", os.ErrNotExist},
57+
{bytes.ErrTooLarge, -1, "System Error", bytes.ErrTooLarge},
58+
{nil, 0, "ok", nil},
59+
{New(10, "testing"), 10, "testing", nil},
60+
{NewCode(20), 20, "", nil},
61+
{Wrap(New(10, "testing")).Comment("error comment"), 10, "testing: error comment", nil},
62+
{Wrap(New(10, "testing")).Err(wrapE), 10, "testing", wrapE},
63+
{Wrap(New(10, "testing %s")).Err(wrapE).Format("format"), 10, "testing format", wrapE},
64+
} {
65+
t.Run("", func(t *testing.T) {
66+
code, message, err := Parse(v.err)
67+
if code != v.code {
68+
t.Errorf("code:%d != v.code:%d", code, v.code)
69+
}
70+
71+
if message != v.message {
72+
t.Errorf("message:%s != v.message:%s", message, v.message)
73+
}
74+
75+
if v.wrapErr != nil && !errors.Is(err, v.wrapErr) {
76+
t.Errorf("errors.Is(err:%v, v.wrapErr:%v) != true", err, v.wrapErr)
77+
}
78+
79+
if _, ok := v.wrapErr.(Errno); !ok && v.wrapErr != nil && errors.Unwrap(err) != v.wrapErr {
80+
t.Errorf("errors.Unwrap(err:%v)!=v.wrapErr:%v", err, v.wrapErr)
81+
}
82+
})
83+
}
84+
}

errno/errno.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// MIT License
2+
// Copyright (c) 2020 Qi Yin <[email protected]>
3+
4+
package errno
5+
6+
import (
7+
"fmt"
8+
)
9+
10+
// Errno 包含错误码和错误消息的接口,适用于应用程序错误
11+
type Errno interface {
12+
error
13+
14+
// Code 返回错误编码
15+
Code() int
16+
17+
// Message 返回错误消息文本
18+
Message() string
19+
}
20+
21+
// errno 包含错误码和错误消息,适用于应用程序错误
22+
type errno struct {
23+
code int
24+
message string
25+
}
26+
27+
// Code 返回错误编码
28+
func (e *errno) Code() int {
29+
return e.code
30+
}
31+
32+
// Message 返回错误消息文本
33+
func (e *errno) Message() string {
34+
return e.message
35+
}
36+
37+
// Error error 接口实现,格式化 Errno 信息
38+
func (e *errno) Error() string {
39+
return fmt.Sprintf("Error - code: %d, message: %s", e.code, e.message)
40+
}
41+
42+
// New 给定错误码及错误格式化文本,返回一个 Errno
43+
// 每 New 一次返回一个新的 Errno, 即使 code 和 message 相同
44+
func New(code int, message string) Errno {
45+
return &errno{code: code, message: message}
46+
}
47+
48+
// NewCode 给定错误码,返回一个 Errno
49+
// 每 New 一次返回一个新的 Errno, 即使 code 相同
50+
func NewCode(code int) Errno {
51+
return &errno{code: code}
52+
}

errno/errno_test.go

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package errno
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
)
7+
8+
func TestNew(t *testing.T) {
9+
t1 := New(0, "ok")
10+
t2 := New(0, "ok")
11+
t3 := New(-1, "message")
12+
13+
if t1 == t2 {
14+
t.Errorf("multiple New returns the same instance %p == %p", t1, t2)
15+
}
16+
17+
if e, ok := t1.(*errno); !ok {
18+
t.Error("t1 is not an instance of *errno")
19+
} else if e.code != 0 {
20+
t.Errorf("t1.code %d != 0", e.code)
21+
} else if e.message != "ok" {
22+
t.Errorf("t1.message %s != ok", e.message)
23+
}
24+
25+
if e, ok := t2.(*errno); !ok {
26+
t.Error("t2 is not an instance of *errno")
27+
} else if e.code != 0 {
28+
t.Errorf("t2.code %d != 0", e.code)
29+
} else if e.message != "ok" {
30+
t.Errorf("t2.message %s != ok", e.message)
31+
}
32+
33+
if e, ok := t3.(*errno); !ok {
34+
t.Error("t3 is not an instance of *errno")
35+
} else if e.code != -1 {
36+
t.Errorf("t3.code %d != -1", e.code)
37+
} else if e.message != "message" {
38+
t.Errorf("t3.message %s != message", e.message)
39+
}
40+
}
41+
42+
func TestNewCode(t *testing.T) {
43+
t1 := NewCode(0)
44+
t2 := NewCode(0)
45+
t3 := NewCode(-1)
46+
47+
if t1 == t2 {
48+
t.Errorf("multiple NewCode returns the same instance %p == %p", t1, t2)
49+
}
50+
51+
if e, ok := t1.(*errno); !ok {
52+
t.Error("t1 is not an instance of *errno")
53+
} else if e.code != 0 {
54+
t.Errorf("t1.code %d != 0", e.code)
55+
} else if e.message != "" {
56+
t.Errorf("t1.message %s not empty", e.message)
57+
}
58+
59+
if e, ok := t2.(*errno); !ok {
60+
t.Error("t2 is not an instance of *errno")
61+
} else if e.code != 0 {
62+
t.Errorf("t2.code %d != 0", e.code)
63+
} else if e.message != "" {
64+
t.Errorf("t2.message %s not empty ", e.message)
65+
}
66+
67+
if e, ok := t3.(*errno); !ok {
68+
t.Error("t3 is not an instance of *errno")
69+
} else if e.code != -1 {
70+
t.Errorf("t3.code %d != -1", e.code)
71+
} else if e.message != "" {
72+
t.Errorf("t3.message %s not empty", e.message)
73+
}
74+
}
75+
76+
func TestErrno_Code(t *testing.T) {
77+
t1 := NewCode(1)
78+
t2 := NewCode(2)
79+
t3 := NewCode(-1)
80+
81+
if t1.Code() != 1 {
82+
t.Errorf("t1.Code() %d != 1", t1.Code())
83+
}
84+
85+
if t2.Code() != 2 {
86+
t.Errorf("t2.Code() %d != 2", t2.Code())
87+
}
88+
89+
if t3.Code() != -1 {
90+
t.Errorf("t3.Code() %d != -1", t3.Code())
91+
}
92+
}
93+
94+
func TestErrno_Message(t *testing.T) {
95+
t1 := New(1, "Error Message 1")
96+
t2 := New(2, "Error Message 2")
97+
t3 := New(-1, "System Error")
98+
99+
if t1.Message() != "Error Message 1" {
100+
t.Errorf("t1.Message() %s != 'Error Message 1'", t1.Message())
101+
}
102+
103+
if t2.Message() != "Error Message 2" {
104+
t.Errorf("t2.Message() %s != 'Error Message 2'", t2.Message())
105+
}
106+
107+
if t3.Message() != "System Error" {
108+
t.Errorf("t3.Message() %s != 'System Error'", t3.Message())
109+
}
110+
}
111+
112+
func TestErrno_Error(t *testing.T) {
113+
err := New(-1, "System Error")
114+
if err.Error() != "Error - code: -1, message: System Error" {
115+
t.Errorf("err.Error():'%s' != 'Error - code: -1, message: System Error'", err.Error())
116+
}
117+
118+
if fmt.Sprintf("%v", err) != "Error - code: -1, message: System Error" {
119+
t.Errorf("fmt.Sprintf(\"%%v\", err):'%s' != 'Error - code: -1, message: System Error'", fmt.Sprintf("%v", err))
120+
}
121+
}

errno/error.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package errno
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"reflect"
7+
)
8+
9+
type wrapError struct {
10+
old *wrapError
11+
new error
12+
msg string
13+
}
14+
15+
func (w *wrapError) Wrap(err error) *wrapError {
16+
if err == nil {
17+
return w
18+
}
19+
20+
// 第一层错误为 Errno,不设置msg
21+
if w == nil {
22+
if _, ok := err.(Errno); ok {
23+
return &wrapError{new: err}
24+
}
25+
}
26+
27+
var msg string
28+
if w == nil || w.msg == "" {
29+
msg = fmt.Sprintf("%v", err)
30+
} else {
31+
msg = fmt.Sprintf("%s, %v", w.msg, err)
32+
}
33+
34+
return &wrapError{old: w, new: err, msg: msg}
35+
}
36+
37+
func (w *wrapError) Error() string {
38+
if w == nil {
39+
return ""
40+
}
41+
return w.msg
42+
}
43+
44+
func (w *wrapError) Is(target error) bool {
45+
if w == target {
46+
return true
47+
}
48+
for w != nil {
49+
if errors.Is(w.new, target) {
50+
return true
51+
}
52+
53+
w = w.old
54+
}
55+
56+
return false
57+
}
58+
59+
func (w *wrapError) As(target interface{}) bool {
60+
for w != nil {
61+
if errors.As(w.new, target) {
62+
return true
63+
}
64+
65+
w = w.old
66+
}
67+
68+
return false
69+
}
70+
71+
func (w *wrapError) Unwrap() error {
72+
if w == nil {
73+
return nil
74+
}
75+
return w.new
76+
}
77+
78+
var errorType = reflect.TypeOf((*error)(nil)).Elem()

0 commit comments

Comments
 (0)