Skip to content
This repository was archived by the owner on Dec 1, 2021. It is now read-only.

Commit c0c662e

Browse files
committed
Use fmt.Formatter throughout (#40)
* Use fmt.Formatter throughout Replace Fprintf with fmt.Formatter logic on various types. This effectively guts Fprint to be just a recursive call down the err.Cause chain. The next step will be to move this recursive logic into wrapper.Format. This change necessitates adding types for the error impls returned from New and Errorf, and Wrap and Wrapf, respectively. The name of the latter type is acceptable, the former is not and alternative suggestions are encouraged. - Remove cause.Format - Added fmt.Formatter tests. The location is temporary, once this PR is merged and Fprint removed they will be merged into errors_test.go
1 parent 4ffae16 commit c0c662e

File tree

3 files changed

+115
-42
lines changed

3 files changed

+115
-42
lines changed

errors.go

Lines changed: 42 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -54,30 +54,44 @@
5454
package errors
5555

5656
import (
57-
"errors"
5857
"fmt"
5958
"io"
6059
)
6160

61+
// _error is an error implementation returned by New and Errorf
62+
// that implements its own fmt.Formatter.
63+
type _error struct {
64+
msg string
65+
*stack
66+
}
67+
68+
func (e _error) Error() string { return e.msg }
69+
70+
func (e _error) Format(s fmt.State, verb rune) {
71+
switch verb {
72+
case 'v':
73+
if s.Flag('+') {
74+
fmt.Fprintf(s, "%+v: ", e.Stacktrace()[0])
75+
}
76+
fallthrough
77+
case 's':
78+
io.WriteString(s, e.msg)
79+
}
80+
}
81+
6282
// New returns an error that formats as the given text.
6383
func New(text string) error {
64-
return struct {
65-
error
66-
*stack
67-
}{
68-
errors.New(text),
84+
return _error{
85+
text,
6986
callers(),
7087
}
7188
}
7289

7390
// Errorf formats according to a format specifier and returns the string
7491
// as a value that satisfies error.
7592
func Errorf(format string, args ...interface{}) error {
76-
return struct {
77-
error
78-
*stack
79-
}{
80-
fmt.Errorf(format, args...),
93+
return _error{
94+
fmt.Sprintf(format, args...),
8195
callers(),
8296
}
8397
}
@@ -87,19 +101,26 @@ type cause struct {
87101
msg string
88102
}
89103

90-
func (c cause) Error() string { return fmt.Sprintf("%v", c) }
104+
func (c cause) Error() string { return fmt.Sprintf("%s: %v", c.msg, c.Cause()) }
91105
func (c cause) Cause() error { return c.cause }
92106

93-
func (c cause) Format(s fmt.State, verb rune) {
107+
// wrapper is an error implementation returned by Wrap and Wrapf
108+
// that implements its own fmt.Formatter.
109+
type wrapper struct {
110+
cause
111+
*stack
112+
}
113+
114+
func (w wrapper) Format(s fmt.State, verb rune) {
94115
switch verb {
95116
case 'v':
96117
if s.Flag('+') {
97-
io.WriteString(s, c.msg)
118+
fmt.Fprintf(s, "%+v: %s", w.Stacktrace()[0], w.cause.msg)
98119
return
99120
}
100121
fallthrough
101122
case 's':
102-
fmt.Fprintf(s, "%s: %v", c.msg, c.Cause())
123+
io.WriteString(s, w.Error())
103124
}
104125
}
105126

@@ -109,15 +130,12 @@ func Wrap(err error, message string) error {
109130
if err == nil {
110131
return nil
111132
}
112-
return struct {
113-
cause
114-
*stack
115-
}{
116-
cause{
133+
return wrapper{
134+
cause: cause{
117135
cause: err,
118136
msg: message,
119137
},
120-
callers(),
138+
stack: callers(),
121139
}
122140
}
123141

@@ -127,15 +145,12 @@ func Wrapf(err error, format string, args ...interface{}) error {
127145
if err == nil {
128146
return nil
129147
}
130-
return struct {
131-
cause
132-
*stack
133-
}{
134-
cause{
148+
return wrapper{
149+
cause: cause{
135150
cause: err,
136151
msg: fmt.Sprintf(format, args...),
137152
},
138-
callers(),
153+
stack: callers(),
139154
}
140155
}
141156

@@ -179,20 +194,8 @@ func Cause(err error) error {
179194
//
180195
// Deprecated: Fprint will be removed in version 0.7.
181196
func Fprint(w io.Writer, err error) {
182-
type stacktrace interface {
183-
Stacktrace() []Frame
184-
}
185-
186197
for err != nil {
187-
switch err := err.(type) {
188-
case stacktrace:
189-
frame := err.Stacktrace()[0]
190-
fmt.Fprintf(w, "%+v: ", frame)
191-
default:
192-
// de nada
193-
}
194198
fmt.Fprintf(w, "%+v\n", err)
195-
196199
cause, ok := err.(causer)
197200
if !ok {
198201
break

example_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ func ExampleNew() {
1414
// Output: whoops
1515
}
1616

17-
func ExampleNew_fprint() {
17+
func ExampleNew_printf() {
1818
err := errors.New("whoops")
19-
errors.Fprint(os.Stdout, err)
19+
fmt.Printf("%+v", err)
2020

2121
// Output: github.com/pkg/errors/example_test.go:18: whoops
2222
}
@@ -65,7 +65,7 @@ func ExampleWrapf() {
6565

6666
func ExampleErrorf() {
6767
err := errors.Errorf("whoops: %s", "foo")
68-
errors.Fprint(os.Stdout, err)
68+
fmt.Printf("%+v", err)
6969

7070
// Output: github.com/pkg/errors/example_test.go:67: whoops: foo
7171
}

format_test.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package errors
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
)
7+
8+
func TestFormat(t *testing.T) {
9+
tests := []struct {
10+
error
11+
format string
12+
want string
13+
}{{
14+
15+
New("error"),
16+
"%s",
17+
"error",
18+
}, {
19+
New("error"),
20+
"%v",
21+
"error",
22+
}, {
23+
New("error"),
24+
"%+v",
25+
"github.com/pkg/errors/format_test.go:23: error",
26+
}, {
27+
Errorf("%s", "error"),
28+
"%s",
29+
"error",
30+
}, {
31+
Errorf("%s", "error"),
32+
"%v",
33+
"error",
34+
}, {
35+
Errorf("%s", "error"),
36+
"%+v",
37+
"github.com/pkg/errors/format_test.go:35: error",
38+
}, {
39+
Wrap(New("error"), "error2"),
40+
"%s",
41+
"error2: error",
42+
}, {
43+
Wrap(New("error"), "error2"),
44+
"%v",
45+
"error2: error",
46+
}, {
47+
Wrap(New("error"), "error2"),
48+
"%+v",
49+
"github.com/pkg/errors/format_test.go:47: error2",
50+
}, {
51+
Wrapf(New("error"), "error%d", 2),
52+
"%s",
53+
"error2: error",
54+
}, {
55+
Wrapf(New("error"), "error%d", 2),
56+
"%v",
57+
"error2: error",
58+
}, {
59+
Wrapf(New("error"), "error%d", 2),
60+
"%+v",
61+
"github.com/pkg/errors/format_test.go:59: error2",
62+
}}
63+
64+
for _, tt := range tests {
65+
got := fmt.Sprintf(tt.format, tt.error)
66+
if got != tt.want {
67+
t.Errorf("fmt.Sprintf(%q, err): got: %q, want: %q", tt.format, got, tt.want)
68+
}
69+
}
70+
}

0 commit comments

Comments
 (0)