Add HTTP /v1 MVC, TCP send path, groups with server-side member-avatar collage, object-storage upload, and friends/favorites around the architecture-v3 layout. Co-authored-by: Cursor <cursoragent@cursor.com>
109 lines
2.7 KiB
Go
109 lines
2.7 KiB
Go
package errs
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
)
|
|
|
|
// AppError is the process-wide error type. Transport maps Code/Msg into the {code,msg,data} envelope.
|
|
// InnerMsg is log-only and must not be sent to clients.
|
|
type AppError struct {
|
|
Code string `json:"code"`
|
|
Msg string `json:"msg"`
|
|
InnerMsg string `json:"inner_msg"`
|
|
LogPrint bool `json:"log_print"`
|
|
custom bool
|
|
}
|
|
|
|
func (e *AppError) Error() string {
|
|
if e == nil {
|
|
return ""
|
|
}
|
|
return fmt.Sprintf("[%s]:%s:%s", e.Code, e.Msg, e.InnerMsg)
|
|
}
|
|
|
|
func NewError(code, msg, innerMsg string) *AppError {
|
|
return &AppError{Code: code, Msg: msg, InnerMsg: innerMsg}
|
|
}
|
|
|
|
func NewErrorPrint(code, msg, innerMsg string) *AppError {
|
|
return &AppError{Code: code, Msg: msg, InnerMsg: innerMsg, LogPrint: true}
|
|
}
|
|
|
|
func (e *AppError) CodeMsg() (string, string, string) {
|
|
if e == nil {
|
|
return "", "", ""
|
|
}
|
|
return e.Code, e.Msg, e.InnerMsg
|
|
}
|
|
|
|
// WithMsg sets the user-visible message.
|
|
func (e *AppError) WithMsg(msg string) *AppError {
|
|
if e == nil {
|
|
return nil
|
|
}
|
|
return &AppError{Code: e.Code, Msg: msg, InnerMsg: e.InnerMsg, LogPrint: e.LogPrint, custom: true}
|
|
}
|
|
|
|
// CustomMsg is true when Msg was set by WithMsg and must not be replaced by i18n.
|
|
func (e *AppError) CustomMsg() bool {
|
|
return e != nil && e.custom && e.Msg != ""
|
|
}
|
|
|
|
// WithInnerMsg sets the log-only internal message.
|
|
func (e *AppError) WithInnerMsg(innerMsg string) *AppError {
|
|
if e == nil {
|
|
return nil
|
|
}
|
|
return &AppError{Code: e.Code, Msg: e.Msg, InnerMsg: innerMsg, LogPrint: e.LogPrint, custom: e.custom}
|
|
}
|
|
|
|
// WithError sets InnerMsg from a downstream error.
|
|
func (e *AppError) WithError(serviceName string, err error) *AppError {
|
|
if e == nil {
|
|
return nil
|
|
}
|
|
return &AppError{
|
|
Code: e.Code,
|
|
Msg: e.Msg,
|
|
InnerMsg: fmt.Sprintf("[%s] err:%v", serviceName, err),
|
|
LogPrint: e.LogPrint,
|
|
custom: e.custom,
|
|
}
|
|
}
|
|
|
|
func (e *AppError) Is(target error) bool {
|
|
t, ok := target.(*AppError)
|
|
if !ok || e == nil || t == nil {
|
|
return false
|
|
}
|
|
return e.Code == t.Code
|
|
}
|
|
|
|
func AsAppError(err error) (*AppError, bool) {
|
|
var app *AppError
|
|
if errors.As(err, &app) && app != nil {
|
|
return app, true
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
// FromRPCError maps a downstream call error. This process has no gRPC; context and AppError are used.
|
|
func FromRPCError(serviceName string, err error) *AppError {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
if app, ok := AsAppError(err); ok {
|
|
return app
|
|
}
|
|
switch {
|
|
case errors.Is(err, context.DeadlineExceeded):
|
|
return ErrTimeout.WithInnerMsg(fmt.Sprintf("[%s] timeout", serviceName))
|
|
case errors.Is(err, context.Canceled):
|
|
return ErrOperatFail.WithInnerMsg(fmt.Sprintf("[%s] canceled", serviceName))
|
|
default:
|
|
return ErrInner.WithInnerMsg(fmt.Sprintf("[%s] unexpected error: %v", serviceName, err))
|
|
}
|
|
}
|