package util import ( "errors" "fmt" ) // Code is a string error category used across the application. type Code string const ( CodeBadRequest Code = "bad_request" CodeNotFound Code = "not_found" CodeConflict Code = "conflict" CodeInternal Code = "internal" ) // AppError is a typed error carrying a Code and optional wrapped error. type AppError struct { Code Code Message string Err error } func (e *AppError) Error() string { if e.Err != nil { return fmt.Sprintf("%s: %s: %v", e.Code, e.Message, e.Err) } return fmt.Sprintf("%s: %s", e.Code, e.Message) } func (e *AppError) Unwrap() error { return e.Err } // New returns a new AppError with the given code and message. func New(code Code, message string) error { return &AppError{Code: code, Message: message} } // Wrap returns a new AppError wrapping err with the given code and message. func Wrap(code Code, message string, err error) error { return &AppError{Code: code, Message: message, Err: err} } // CodeOf returns the Code embedded in err. nil returns CodeInternal; // non-AppError errors also default to CodeInternal. func CodeOf(err error) Code { if err == nil { return CodeInternal } var ae *AppError if errors.As(err, &ae) { return ae.Code } return CodeInternal }