summaryrefslogtreecommitdiff
path: root/vendor/codeberg.org/gruf/go-errors/v2/callers.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/codeberg.org/gruf/go-errors/v2/callers.go')
-rw-r--r--vendor/codeberg.org/gruf/go-errors/v2/callers.go102
1 files changed, 102 insertions, 0 deletions
diff --git a/vendor/codeberg.org/gruf/go-errors/v2/callers.go b/vendor/codeberg.org/gruf/go-errors/v2/callers.go
new file mode 100644
index 000000000..77a2c1c1b
--- /dev/null
+++ b/vendor/codeberg.org/gruf/go-errors/v2/callers.go
@@ -0,0 +1,102 @@
+package errors
+
+import (
+ "encoding/json"
+ "runtime"
+ "strconv"
+ "strings"
+ "unsafe"
+)
+
+// Callers is a stacktrace of caller PCs.
+type Callers []uintptr
+
+// GetCallers returns a Callers slice of PCs, of at most 'depth'.
+func GetCallers(skip int, depth int) Callers {
+ rpc := make([]uintptr, depth)
+ n := runtime.Callers(skip+1, rpc)
+ return Callers(rpc[0:n])
+}
+
+// Frames fetches runtime frames for a slice of caller PCs.
+func (f Callers) Frames() []runtime.Frame {
+ // Allocate expected frames slice
+ frames := make([]runtime.Frame, 0, len(f))
+
+ // Get frames iterator for PCs
+ iter := runtime.CallersFrames(f)
+
+ for {
+ // Get next frame in iter
+ frame, ok := iter.Next()
+ if !ok {
+ break
+ }
+
+ // Append to frames slice
+ frames = append(frames, frame)
+ }
+
+ return frames
+}
+
+// MarshalJSON implements json.Marshaler to provide an easy, simply default.
+func (f Callers) MarshalJSON() ([]byte, error) {
+ // JSON-able frame type
+ type frame struct {
+ Func string `json:"func"`
+ File string `json:"file"`
+ Line int `json:"line"`
+ }
+
+ // Allocate expected frames slice
+ frames := make([]frame, 0, len(f))
+
+ // Get frames iterator for PCs
+ iter := runtime.CallersFrames(f)
+
+ for {
+ // Get next frame
+ f, ok := iter.Next()
+ if !ok {
+ break
+ }
+
+ // Append to frames slice
+ frames = append(frames, frame{
+ Func: funcname(f.Function),
+ File: f.File,
+ Line: f.Line,
+ })
+ }
+
+ // marshal converted frames
+ return json.Marshal(frames)
+}
+
+// String will return a simple string representation of receiving Callers slice.
+func (f Callers) String() string {
+ // Guess-timate to reduce allocs
+ buf := make([]byte, 0, 64*len(f))
+
+ // Convert to frames
+ frames := f.Frames()
+
+ for i := 0; i < len(frames); i++ {
+ frame := frames[i]
+
+ // Append formatted caller info
+ funcname := funcname(frame.Function)
+ buf = append(buf, funcname+"()\n\t"+frame.File+":"...)
+ buf = strconv.AppendInt(buf, int64(frame.Line), 10)
+ buf = append(buf, '\n')
+ }
+
+ return *(*string)(unsafe.Pointer(&buf))
+}
+
+// funcname splits a function name with pkg from its path prefix.
+func funcname(name string) string {
+ i := strings.LastIndex(name, "/")
+ return name[i+1:]
+}