Files
imgproxy/ierrors/errors.go
2025-02-17 22:11:40 +03:00

149 lines
2.3 KiB
Go

package ierrors
import (
"fmt"
"net/http"
"runtime"
"strings"
)
type Option func(*Error)
type Error struct {
err error
prefix string
statusCode int
publicMessage string
shouldReport bool
stack []uintptr
}
func (e *Error) Error() string {
if len(e.prefix) > 0 {
return fmt.Sprintf("%s: %s", e.prefix, e.err.Error())
}
return e.err.Error()
}
func (e *Error) Unwrap() error {
return e.err
}
func (e *Error) Cause() error {
return e.err
}
func (e *Error) StatusCode() int {
if e.statusCode <= 0 {
return http.StatusInternalServerError
}
return e.statusCode
}
func (e *Error) PublicMessage() string {
if len(e.publicMessage) == 0 {
return "Internal error"
}
return e.publicMessage
}
func (e *Error) ShouldReport() bool {
return e.shouldReport
}
func (e *Error) StackTrace() []uintptr {
return e.stack
}
func (e *Error) Callers() []uintptr {
return e.stack
}
func (e *Error) FormatStackLines() []string {
lines := make([]string, len(e.stack))
for i, pc := range e.stack {
f := runtime.FuncForPC(pc)
file, line := f.FileLine(pc)
lines[i] = fmt.Sprintf("%s:%d %s", file, line, f.Name())
}
return lines
}
func (e *Error) FormatStack() string {
return strings.Join(e.FormatStackLines(), "\n")
}
func Wrap(err error, stackSkip int, opts ...Option) *Error {
if err == nil {
return nil
}
var e *Error
if ierr, ok := err.(*Error); ok {
// if we have some options, we need to copy the error to not modify the original one
if len(opts) > 0 {
ecopy := *ierr
e = &ecopy
} else {
return ierr
}
} else {
e = &Error{
err: err,
shouldReport: true,
}
}
for _, opt := range opts {
opt(e)
}
if len(e.stack) == 0 {
e.stack = callers(stackSkip + 1)
}
return e
}
func WithStatusCode(code int) Option {
return func(e *Error) {
e.statusCode = code
}
}
func WithPublicMessage(msg string) Option {
return func(e *Error) {
e.publicMessage = msg
}
}
func WithPrefix(prefix string) Option {
return func(e *Error) {
if len(e.prefix) > 0 {
e.prefix = fmt.Sprintf("%s: %s", prefix, e.prefix)
} else {
e.prefix = prefix
}
}
}
func WithShouldReport(report bool) Option {
return func(e *Error) {
e.shouldReport = report
}
}
func callers(skip int) []uintptr {
stack := make([]uintptr, 10)
n := runtime.Callers(skip+2, stack)
return stack[:n]
}