...
1
2
3
4
5
6
7
8
9
10
11
12
13
14 package errors
15
16 import (
17 "errors"
18 "fmt"
19 "net/http"
20 "regexp"
21 "strconv"
22 "strings"
23 )
24
25
26
27 type CompositeError string
28
29 func (c CompositeError) Error() string {
30 return "kivik: " + string(c[4:])
31 }
32
33
34 func (c CompositeError) HTTPStatus() int {
35 i, _ := strconv.Atoi(string(c[:3]))
36 return i
37 }
38
39
40
41
42
43
44
45 type Error struct {
46
47
48
49 Status int
50
51
52 Message string
53
54
55 Err error
56 }
57
58 var (
59 _ error = &Error{}
60 _ statusCoder = &Error{}
61 )
62
63 func (e *Error) Error() string {
64 if e.Err == nil {
65 return e.msg()
66 }
67 if e.Message == "" {
68 return e.Err.Error()
69 }
70 return e.Message + ": " + e.Err.Error()
71 }
72
73
74
75 func (e *Error) HTTPStatus() int {
76 if e.Status == 0 {
77 return http.StatusInternalServerError
78 }
79 return e.Status
80 }
81
82
83 func (e *Error) Unwrap() error {
84 return e.Err
85 }
86
87
88 func (e *Error) Format(f fmt.State, c rune) {
89 const partsLen = 3
90 parts := make([]string, 0, partsLen)
91 if e.Message != "" {
92 parts = append(parts, e.Message)
93 }
94 if c == 'v' {
95 if f.Flag('+') {
96 parts = append(parts, fmt.Sprintf("%d / %s", e.Status, http.StatusText(e.Status)))
97 }
98 }
99 if e.Err != nil {
100 parts = append(parts, e.Err.Error())
101 }
102 _, _ = fmt.Fprint(f, strings.Join(parts, ": "))
103 }
104
105 func (e *Error) msg() string {
106 switch e.Message {
107 case "":
108 return http.StatusText(e.HTTPStatus())
109 default:
110 return e.Message
111 }
112 }
113
114 type statusCoder interface {
115 HTTPStatus() int
116 }
117
118
119
120
121 func HTTPStatus(err error) int {
122 if err == nil {
123 return 0
124 }
125 var coder statusCoder
126 for {
127 if errors.As(err, &coder) {
128 return coder.HTTPStatus()
129 }
130 if uw := errors.Unwrap(err); uw != nil {
131 err = uw
132 continue
133 }
134 return http.StatusInternalServerError
135 }
136 }
137
138
139
140 func StatusErrorDiff(wantErr string, wantStatus int, err error) string {
141 var (
142 msg string
143 status int
144 )
145 if err != nil {
146 status = HTTPStatus(err)
147 msg = err.Error()
148 }
149 if msg != wantErr || status != wantStatus {
150 return fmt.Sprintf("Unexpected error: %s [%d] (expected: %s [%d])",
151 err, status, wantErr, wantStatus)
152 }
153 return ""
154 }
155
156
157
158 func StatusErrorDiffRE(wantErrRE string, wantStatus int, err error) string {
159 re := regexp.MustCompile(wantErrRE)
160 var (
161 msg string
162 status int
163 )
164 if err != nil {
165 status = HTTPStatus(err)
166 msg = err.Error()
167 }
168 if !re.MatchString(msg) || status != wantStatus {
169 return fmt.Sprintf("Unexpected error: %s [%d] (expected: %s [%d])",
170 err, status, re, wantStatus)
171 }
172 return ""
173 }
174
View as plain text