mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-09-25 21:48:42 -04:00
Bumps [github.com/olekukonko/errors](https://github.com/olekukonko/errors) from 1.2.0 to 1.3.0. - [Commits](https://github.com/olekukonko/errors/compare/v1.2.0...v1.3.0) --- updated-dependencies: - dependency-name: github.com/olekukonko/errors dependency-version: 1.3.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
171 lines
4.5 KiB
Go
171 lines
4.5 KiB
Go
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
|
|
}
|