gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

new_caller.go (2270B)


      1 // GoToSocial
      2 // Copyright (C) GoToSocial Authors admin@gotosocial.org
      3 // SPDX-License-Identifier: AGPL-3.0-or-later
      4 //
      5 // This program is free software: you can redistribute it and/or modify
      6 // it under the terms of the GNU Affero General Public License as published by
      7 // the Free Software Foundation, either version 3 of the License, or
      8 // (at your option) any later version.
      9 //
     10 // This program is distributed in the hope that it will be useful,
     11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
     12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13 // GNU Affero General Public License for more details.
     14 //
     15 // You should have received a copy of the GNU Affero General Public License
     16 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
     17 
     18 //go:build !noerrcaller
     19 
     20 package gtserror
     21 
     22 import (
     23 	"errors"
     24 	"fmt"
     25 	"runtime"
     26 	"strings"
     27 )
     28 
     29 // Caller returns whether created errors will prepend calling function name.
     30 const Caller = true
     31 
     32 // cerror wraps an error with a string
     33 // prefix of the caller function name.
     34 type cerror struct {
     35 	c string
     36 	e error
     37 }
     38 
     39 func (ce *cerror) Error() string {
     40 	msg := ce.e.Error()
     41 	return ce.c + ": " + msg
     42 }
     43 
     44 func (ce *cerror) Unwrap() error {
     45 	return ce.e
     46 }
     47 
     48 // newAt is the same as New() but allows specifying calldepth.
     49 func newAt(calldepth int, msg string) error {
     50 	return &cerror{
     51 		c: caller(calldepth + 1),
     52 		e: errors.New(msg),
     53 	}
     54 }
     55 
     56 // newfAt is the same as Newf() but allows specifying calldepth.
     57 func newfAt(calldepth int, msgf string, args ...any) error {
     58 	return &cerror{
     59 		c: caller(calldepth + 1),
     60 		e: fmt.Errorf(msgf, args...),
     61 	}
     62 }
     63 
     64 // caller fetches the calling function name, skipping 'depth'. Results are cached per PC.
     65 func caller(depth int) string {
     66 	var pcs [1]uintptr
     67 
     68 	// Fetch calling function using calldepth
     69 	_ = runtime.Callers(depth, pcs[:])
     70 	fn := runtime.FuncForPC(pcs[0])
     71 
     72 	if fn == nil {
     73 		return ""
     74 	}
     75 
     76 	// Get func name.
     77 	name := fn.Name()
     78 
     79 	// Drop everything but but function name itself
     80 	if idx := strings.LastIndexByte(name, '.'); idx >= 0 {
     81 		name = name[idx+1:]
     82 	}
     83 
     84 	const params = `[...]`
     85 
     86 	// Drop any generic type parameter markers
     87 	if idx := strings.Index(name, params); idx >= 0 {
     88 		name = name[:idx] + name[idx+len(params):]
     89 	}
     90 
     91 	return name
     92 }