-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathutils.go
More file actions
170 lines (156 loc) · 4.55 KB
/
utils.go
File metadata and controls
170 lines (156 loc) · 4.55 KB
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
package errors
import (
"database/sql"
"fmt"
"reflect"
"runtime"
"strings"
)
// captureStack captures a stack trace with the configured depth.
// Immune to inlining: captures from frame 1 and trims by shifting within
// the same buffer so the pooled slice always retains its full capacity.
func captureStack(skip int) []uintptr {
buf := stackPool.Get().([]uintptr)
buf = buf[:cap(buf)]
// Capture from frame 1 (skipping runtime.Callers itself).
// captureStack can never be inlined because it calls runtime.Callers,
// so buf[0] is always captureStack regardless of compiler inlining above.
n := runtime.Callers(1, buf)
if n == 0 {
stackPool.Put(buf)
return nil
}
// Trim leading internal frames in-place using copy, preserving the
// buffer's full capacity so the pool never fills with shrinking slices.
// skip+1: +1 for captureStack itself (always buf[0]).
trimmed := skip + 1
if trimmed >= n {
stackPool.Put(buf)
return nil
}
length := n - trimmed
// Shift the useful frames to the start of buf — same backing array,
// same capacity, zero allocation.
copy(buf, buf[trimmed:n])
return buf[:length]
}
// min returns the smaller of two integers.
func min(a, b int) int {
if a < b {
return a
}
return b
}
// clearMap removes all entries from a map without reallocating it.
func clearMap(m map[string]interface{}) {
for k := range m {
delete(m, k)
}
}
// sqlNull detects if a value represents a SQL NULL type.
func sqlNull(v interface{}) bool {
if v == nil {
return true
}
switch val := v.(type) {
case sql.NullString:
return !val.Valid
case sql.NullTime:
return !val.Valid
case sql.NullInt64:
return !val.Valid
case sql.NullBool:
return !val.Valid
case sql.NullFloat64:
return !val.Valid
default:
return false
}
}
// getFuncName extracts the function name from an interface value.
// Returns "unknown" if the input is nil or invalid.
func getFuncName(fn interface{}) string {
if fn == nil {
return "unknown"
}
fullName := runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name()
return strings.TrimPrefix(fullName, ".")
}
// isInternalFrame reports whether a stack frame belongs to this library's
// internals and should be filtered from user-visible stack traces.
//
// Rules:
// - runtime.* and reflect.* are always internal.
// - _test.go files are NEVER internal: test functions must survive
// filtering so that assertions like "stack contains testing.tRunner"
// and "stack contains TestErrorTraceStackContent" can pass.
// - Source files under github.com/olekukonko/errors/ (errors.go, utils.go,
// helper.go, retry.go, multi_error.go) are internal.
func isInternalFrame(frame runtime.Frame) bool {
if strings.HasPrefix(frame.Function, "runtime.") || strings.HasPrefix(frame.Function, "reflect.") {
return true
}
// Exempt test files before the path-prefix check: errors_test.go lives
// at github.com/olekukonko/errors/errors_test.go which contains the
// "errors" suffix and would otherwise be incorrectly filtered.
if strings.HasSuffix(frame.File, "_test.go") {
return false
}
suffixes := []string{
"errors",
"utils",
"helper",
"retry",
"multi",
}
for _, v := range suffixes {
if strings.Contains(frame.File, fmt.Sprintf("github.com/olekukonko/errors/%s", v)) {
return true
}
}
return false
}
// FormatError returns a formatted string representation of an error.
func FormatError(err error) string {
if err == nil {
return "<nil>"
}
var sb strings.Builder
if e, ok := err.(*Error); ok {
sb.WriteString(fmt.Sprintf("Error: %s\n", e.Error()))
if e.name != "" {
sb.WriteString(fmt.Sprintf("Name: %s\n", e.name))
}
if ctx := e.Context(); len(ctx) > 0 {
sb.WriteString("Context:\n")
for k, v := range ctx {
sb.WriteString(fmt.Sprintf("\t%s: %v\n", k, v))
}
}
if stack := e.Stack(); len(stack) > 0 {
sb.WriteString("Stack Trace:\n")
for _, frame := range stack {
sb.WriteString(fmt.Sprintf("\t%s\n", frame))
}
}
if e.cause != nil {
sb.WriteString(fmt.Sprintf("Caused by: %s\n", FormatError(e.cause)))
}
} else {
sb.WriteString(fmt.Sprintf("Error: %s\n", err.Error()))
}
return sb.String()
}
// Caller returns the file, line, and function name of the caller at skip level.
// Skip=0 returns the caller of this function, 1 returns its caller, etc.
func Caller(skip int) (file string, line int, function string) {
configMu.RLock()
defer configMu.RUnlock()
var pcs [1]uintptr
n := runtime.Callers(skip+2, pcs[:])
if n == 0 {
return "", 0, "unknown"
}
frame, _ := runtime.CallersFrames(pcs[:n]).Next()
return frame.File, frame.Line, frame.Function
}