github.com/qioalice/ekago/v3@v3.3.2-0.20221202205325-5c262d586ee4/ekasys/stackframe.go (about)

     1  // Copyright © 2019. All rights reserved.
     2  // Author: Ilya Yuryevich.
     3  // Contacts: iyuryevich@pm.me, https://github.com/qioalice
     4  // License: https://opensource.org/licenses/MIT
     5  
     6  package ekasys
     7  
     8  import (
     9  	"path/filepath"
    10  	"runtime"
    11  
    12  	"github.com/qioalice/ekago/v3/ekastr"
    13  )
    14  
    15  // StackFrame represents one stack level (frame/item).
    16  // It general purpose is runtime.Frame type extending.
    17  type StackFrame struct {
    18  	runtime.Frame
    19  
    20  	// Format is formatted string representation of the current stack frame:
    21  	// "<package>/<func> (<short_file>:<file_line>) <full_package_path>".
    22  	//
    23  	// Note, stack frame is not formatted by default.
    24  	// It means that this field is empty in 99% cases. If you need to generate
    25  	// formatted string use DoFormat method.
    26  	Format string
    27  
    28  	// FormatFileOffset is the index of
    29  	// "(<short_file>:<file_line>)..." in Format field.
    30  	FormatFileOffset int
    31  
    32  	// FormatFullPathOffset is the index of "<full_package_path>" in Format field.
    33  	FormatFullPathOffset int
    34  }
    35  
    36  // DoFormat generates formatted string representation of the current stack frame,
    37  // saves it to Format field and returns it. Output looks like:
    38  // "<package>/<func> (<short_file>:<file_line>) <full_package_path>".
    39  //
    40  // Does not regenerate formatted string if it's already generated (just returns).
    41  func (f *StackFrame) DoFormat() string {
    42  
    43  	if f.Format == "" {
    44  		f.Format = f.doFormat()
    45  	}
    46  
    47  	return f.Format
    48  }
    49  
    50  // doFormat is a private part of DoFormat() function.
    51  func (f *StackFrame) doFormat() string {
    52  
    53  	fullPackage, fn := filepath.Split(f.Function)
    54  	_, file := filepath.Split(f.File)
    55  
    56  	// we need last package from the fullPackage
    57  	lastPackage := filepath.Base(fullPackage)
    58  
    59  	// need remove last package from fullPackage
    60  	if len(lastPackage)+2 <= len(fullPackage) && lastPackage != "." {
    61  		fullPackage = fullPackage[:len(fullPackage)-len(lastPackage)-2]
    62  	}
    63  
    64  	requiredBufLen := 9 // 2 spaces, '/', '(', ')', ':' + 3 reserved bytes
    65  	requiredBufLen += len(fullPackage) + len(lastPackage) + len(file) + len(fn)
    66  	requiredBufLen += ekastr.PItoa32(int32(f.Line))
    67  
    68  	buf := make([]byte, requiredBufLen)
    69  	offset := 0
    70  
    71  	// maybe 'lastPackage' is version of package?
    72  	if len(lastPackage) > 1 && (lastPackage[0] == 'v' || lastPackage[0] == 'V') {
    73  		is := true
    74  		for i, n := 1, len(lastPackage); i < n && is; i++ {
    75  			if lastPackage[i] < '0' || lastPackage[i] > '9' {
    76  				is = false
    77  			}
    78  		}
    79  		if is {
    80  			lastPackage2 := filepath.Base(fullPackage)
    81  			if len(lastPackage2)+2 <= len(fullPackage) && lastPackage2 != "." {
    82  				fullPackage = fullPackage[:len(fullPackage)-len(lastPackage2)-1]
    83  			}
    84  			copy(buf, lastPackage2)
    85  			offset += len(lastPackage2)
    86  
    87  			buf[offset] = '.'
    88  			offset++
    89  		}
    90  	}
    91  
    92  	copy(buf[offset:], lastPackage)
    93  	offset += len(lastPackage)
    94  
    95  	buf[offset] = '.'
    96  	offset++
    97  
    98  	copy(buf[offset:], fn)
    99  	offset += len(fn)
   100  
   101  	buf[offset] = ' '
   102  	offset++
   103  	buf[offset] = '('
   104  	offset++
   105  
   106  	f.FormatFileOffset = offset - 1
   107  
   108  	copy(buf[offset:], file)
   109  	offset += len(file)
   110  
   111  	buf[offset] = ':'
   112  	offset++
   113  
   114  	offset += ekastr.BItoa32(buf[offset:], int32(f.Line))
   115  
   116  	buf[offset] = ')'
   117  	offset++
   118  	buf[offset] = ' '
   119  	offset++
   120  
   121  	f.FormatFullPathOffset = offset
   122  
   123  	copy(buf[offset:], fullPackage)
   124  	offset += len(fullPackage)
   125  
   126  	return string(buf[:offset])
   127  }