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  }