Redmage/pkg/errs/errs.go

178 lines
3.5 KiB
Go

package errs
import (
"context"
"errors"
"fmt"
"log/slog"
"reflect"
"strings"
"github.com/tigorlazuardi/redmage/pkg/caller"
"github.com/tigorlazuardi/redmage/pkg/log"
)
type Error interface {
error
Message(msg string, args ...any) Error
GetMessage() string
Code(status int) Error
GetCode() int
Caller(pc caller.Caller) Error
GetCaller() caller.Caller
Details(...any) Error
GetDetails() []any
Log(ctx context.Context) Error
}
var _ Error = (*Err)(nil)
type Err struct {
message string
code int
caller caller.Caller
details []any
origin error
}
func (er *Err) LogValue() slog.Value {
values := make([]slog.Attr, 0, 5)
if er.message != "" {
values = append(values, slog.String("message", er.message))
}
if er.code != 0 {
values = append(values, slog.Int("code", er.code))
}
if er.caller.PC != 0 {
values = append(values, slog.Any("origin", er.caller))
}
if len(er.details) > 0 {
values = append(values, slog.Group("details", er.details...))
}
if er.origin == nil {
values = append(values, slog.Any("error", er.origin))
} else {
errGroupValues := make([]slog.Attr, 0, 3)
errGroupValues = append(errGroupValues, slog.String("type", reflect.TypeOf(er.origin).String()))
if lv, ok := er.origin.(slog.LogValuer); ok {
errGroupValues = append(errGroupValues, slog.Attr{Key: "data", Value: lv.LogValue()})
} else {
errGroupValues = append(errGroupValues, slog.String("message", er.origin.Error()), slog.Any("data", er.origin))
}
values = append(values, slog.Attr{Key: "error", Value: slog.GroupValue(errGroupValues...)})
}
return slog.GroupValue(values...)
}
func (er *Err) Error() string {
var (
s = strings.Builder{}
source = er.origin
msg = source.Error()
unwrap = errors.Unwrap(source)
)
if unwrap == nil {
if er.message != "" {
s.WriteString(er.message)
s.WriteString(": ")
}
s.WriteString(msg)
return s.String()
}
for unwrap := errors.Unwrap(source); unwrap != nil; source = unwrap {
originMsg := unwrap.Error()
var write string
if cut, found := strings.CutSuffix(msg, originMsg); found {
write = cut
} else {
write = msg
}
msg = originMsg
if write != "" {
s.WriteString(write)
s.WriteString(": ")
}
}
return s.String()
}
func (er *Err) Message(msg string, args ...any) Error {
er.message = fmt.Sprintf(msg, args...)
return er
}
func (er *Err) GetMessage() string {
return er.message
}
func (er *Err) Code(status int) Error {
er.code = status
return er
}
func (er *Err) GetCode() int {
return er.code
}
func (er *Err) Caller(pc caller.Caller) Error {
er.caller = pc
return er
}
func (er *Err) GetCaller() caller.Caller {
return er.caller
}
func (er *Err) Details(ctx ...any) Error {
er.details = ctx
return er
}
func (er *Err) GetDetails() []any {
return er.details
}
func (er *Err) Log(ctx context.Context) Error {
log.Log(ctx).Caller(er.caller).Error(er.message, "error", er)
return er
}
func Wrap(err error, message string, details ...any) Error {
return &Err{
origin: err,
details: details,
message: message,
caller: caller.New(3),
}
}
func Wrapf(err error, message string, args ...any) Error {
message = fmt.Sprintf(message, args...)
return &Err{
origin: err,
message: message,
caller: caller.New(3),
}
}
func Fail(message string, details ...any) Error {
return &Err{
origin: errors.New(message),
details: details,
caller: caller.New(3),
}
}
func Failf(message string, args ...any) Error {
return &Err{
origin: fmt.Errorf(message, args...),
caller: caller.New(3),
}
}