-
Notifications
You must be signed in to change notification settings - Fork 28
/
format.go
91 lines (83 loc) · 2.38 KB
/
format.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// Licensed under the MIT license, see LICENSE file for details.
package quicktest
import (
"fmt"
"reflect"
"strconv"
"strings"
"unicode/utf8"
"github.com/kr/pretty"
)
// Format formats the given value as a string. It is used to print values in
// test failures unless that's changed by calling C.SetFormat.
func Format(v interface{}) string {
switch v := v.(type) {
case error:
s, ok := checkStringCall(v, v.Error)
if !ok {
return "e<nil>"
}
if msg := fmt.Sprintf("%+v", v); msg != s {
// The error has formatted itself with additional information.
// Leave that as is.
return msg
}
return "e" + quoteString(s)
case fmt.Stringer:
s, ok := checkStringCall(v, v.String)
if !ok {
return "s<nil>"
}
return "s" + quoteString(s)
case string:
return quoteString(v)
case uintptr, uint, uint8, uint16, uint32, uint64:
// Use decimal base (rather than hexadecimal) for representing uint types.
return fmt.Sprintf("%T(%d)", v, v)
}
if bytes, ok := byteSlice(v); ok && bytes != nil && utf8.Valid(bytes) {
// It's a top level slice of bytes that's also valid UTF-8.
// Ideally, this would happen at deeper levels too,
// but this is sufficient for some significant cases
// (json.RawMessage for example).
return fmt.Sprintf("%T(%s)", v, quoteString(string(bytes)))
}
// The pretty.Sprint equivalent does not quote string values.
return fmt.Sprintf("%# v", pretty.Formatter(v))
}
func byteSlice(x interface{}) ([]byte, bool) {
v := reflect.ValueOf(x)
if !v.IsValid() {
return nil, false
}
t := v.Type()
if t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8 {
return v.Bytes(), true
}
return nil, false
}
func quoteString(s string) string {
// TODO think more about what to do about multi-line strings.
if strings.Contains(s, `"`) && !strings.Contains(s, "\n") && strconv.CanBackquote(s) {
return "`" + s + "`"
}
return strconv.Quote(s)
}
// checkStringCall calls f and returns its result, and reports if the call
// succeeded without panicking due to a nil pointer.
// If f panics and v is a nil pointer, it returns false.
func checkStringCall(v interface{}, f func() string) (s string, ok bool) {
defer func() {
err := recover()
if err == nil {
return
}
if val := reflect.ValueOf(v); val.Kind() == reflect.Ptr && val.IsNil() {
ok = false
return
}
panic(err)
}()
return f(), true
}
type formatFunc func(interface{}) string