codeberg.org/gruf/go-errors/v2@v2.3.1/runtime.go (about) 1 package errors 2 3 import ( 4 "encoding/json" 5 "runtime" 6 "strconv" 7 "strings" 8 "unsafe" 9 ) 10 11 // Callers ... 12 type Callers []runtime.Frame 13 14 // MarshalJSON implements json.Marshaler to provide an easy, simple default. 15 func (c Callers) MarshalJSON() ([]byte, error) { 16 // JSON-able frame type 17 type jsonFrame struct { 18 Func string `json:"func"` 19 File string `json:"file"` 20 Line int `json:"line"` 21 } 22 23 // Allocate expected size jsonFrame slice 24 jsonFrames := make([]jsonFrame, len(c)) 25 26 // Convert each to jsonFrame object 27 for i := 0; i < len(c); i++ { 28 frame := c[i] 29 jsonFrames[i] = jsonFrame{ 30 Func: funcName(frame.Func), 31 File: frame.File, 32 Line: frame.Line, 33 } 34 } 35 36 // marshal converted frames 37 return json.Marshal(jsonFrames) 38 } 39 40 // String will return a simple string representation of receiving Callers slice. 41 func (c Callers) String() string { 42 // Guess-timate to reduce allocs 43 buf := make([]byte, 0, 64*len(c)) 44 45 for i := 0; i < len(c); i++ { 46 frame := c[i] 47 48 // Append formatted caller info 49 fn := funcName(frame.Func) 50 buf = append(buf, fn+"()\n\t"+frame.File+":"...) 51 buf = strconv.AppendInt(buf, int64(frame.Line), 10) 52 buf = append(buf, '\n') 53 } 54 55 return *(*string)(unsafe.Pointer(&buf)) 56 } 57 58 // funcName formats a function name to a quickly-readable string. 59 func funcName(fn *runtime.Func) string { 60 if fn == nil { 61 return "" 62 } 63 64 // Get func name 65 // for formatting. 66 name := fn.Name() 67 68 // Drop all but the package name and function name, no mod path 69 if idx := strings.LastIndex(name, "/"); idx >= 0 { 70 name = name[idx+1:] 71 } 72 73 const params = `[...]` 74 75 // Drop any generic type parameter markers 76 if idx := strings.Index(name, params); idx >= 0 { 77 name = name[:idx] + name[idx+len(params):] 78 } 79 80 return name 81 } 82 83 // gatherFrames collates runtime frames from a frame iterator. 84 func gatherFrames(iter *runtime.Frames, n int) Callers { 85 if iter == nil { 86 return nil 87 } 88 frames := make([]runtime.Frame, 0, n) 89 for { 90 f, ok := iter.Next() 91 if !ok { 92 break 93 } 94 frames = append(frames, f) 95 } 96 return frames 97 }