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 }