gotosocial/vendor/codeberg.org/gruf/go-errors/v2/runtime.go

98 lines
2 KiB
Go

package errors
import (
"encoding/json"
"runtime"
"strconv"
"strings"
"unsafe"
)
// Callers ...
type Callers []runtime.Frame
// MarshalJSON implements json.Marshaler to provide an easy, simple default.
func (c Callers) MarshalJSON() ([]byte, error) {
// JSON-able frame type
type jsonFrame struct {
Func string `json:"func"`
File string `json:"file"`
Line int `json:"line"`
}
// Allocate expected size jsonFrame slice
jsonFrames := make([]jsonFrame, len(c))
// Convert each to jsonFrame object
for i := 0; i < len(c); i++ {
frame := c[i]
jsonFrames[i] = jsonFrame{
Func: funcName(frame.Func),
File: frame.File,
Line: frame.Line,
}
}
// marshal converted frames
return json.Marshal(jsonFrames)
}
// String will return a simple string representation of receiving Callers slice.
func (c Callers) String() string {
// Guess-timate to reduce allocs
buf := make([]byte, 0, 64*len(c))
for i := 0; i < len(c); i++ {
frame := c[i]
// Append formatted caller info
fn := funcName(frame.Func)
buf = append(buf, fn+"()\n\t"+frame.File+":"...)
buf = strconv.AppendInt(buf, int64(frame.Line), 10)
buf = append(buf, '\n')
}
return *(*string)(unsafe.Pointer(&buf))
}
// funcName formats a function name to a quickly-readable string.
func funcName(fn *runtime.Func) string {
if fn == nil {
return ""
}
// Get func name
// for formatting.
name := fn.Name()
// Drop all but the package name and function name, no mod path
if idx := strings.LastIndex(name, "/"); idx >= 0 {
name = name[idx+1:]
}
const params = `[...]`
// Drop any generic type parameter markers
if idx := strings.Index(name, params); idx >= 0 {
name = name[:idx] + name[idx+len(params):]
}
return name
}
// gatherFrames collates runtime frames from a frame iterator.
func gatherFrames(iter *runtime.Frames, n int) Callers {
if iter == nil {
return nil
}
frames := make([]runtime.Frame, 0, n)
for {
f, ok := iter.Next()
if !ok {
break
}
frames = append(frames, f)
}
return frames
}