ark-member/internal/lib/error/errors.go

198 lines
4.1 KiB
Go

package error
import (
"errors"
"fmt"
"member/internal/lib/error/code"
"net/http"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// TODO Error要移到common 包
// Scope global variable should be set by service or module
var Scope = code.Unset
type Err struct {
category uint32
code uint32
scope uint32
msg string
internalErr error
}
// Error is the interface of error
// Getter function of private property "msg"
func (e *Err) Error() string {
if e == nil {
return ""
}
// chain the error string if the internal err exists
var internalErrStr string
if e.internalErr != nil {
internalErrStr = e.internalErr.Error()
}
if e.msg != "" {
if internalErrStr != "" {
return fmt.Sprintf("%s: %s", e.msg, internalErrStr)
}
return e.msg
}
generalErrStr := e.GeneralError()
if internalErrStr != "" {
return fmt.Sprintf("%s: %s", generalErrStr, internalErrStr)
}
return generalErrStr
}
// Category getter function of private property "category"
func (e *Err) Category() uint32 {
if e == nil {
return 0
}
return e.category
}
// Scope getter function of private property "scope"
func (e *Err) Scope() uint32 {
if e == nil {
return code.Unset
}
return e.scope
}
// CodeStr returns the string of error code with zero padding
func (e *Err) CodeStr() string {
if e == nil {
return "00000"
}
if e.Category() == code.CatGRPC {
return fmt.Sprintf("%d%04d", e.Scope(), e.Category()+e.Code())
}
return fmt.Sprintf("%d%04d", e.Scope(), e.Code())
}
// Code getter function of private property "code"
func (e *Err) Code() uint32 {
if e == nil {
return code.OK
}
return e.code
}
func (e *Err) FullCode() uint32 {
if e == nil {
return 0
}
if e.Category() == code.CatGRPC {
return e.Scope()*10000 + e.Category() + e.Code()
}
return e.Scope()*10000 + e.Code()
}
// HTTPStatus returns corresponding HTTP status code
func (e *Err) HTTPStatus() int {
if e == nil || e.Code() == code.OK {
return http.StatusOK
}
// determine status code by code
switch e.Code() {
case code.ResourceInsufficient:
// 400
return http.StatusBadRequest
case code.Unauthorized, code.InsufficientPermission:
// 401
return http.StatusUnauthorized
case code.InsufficientQuota:
// 402
return http.StatusPaymentRequired
case code.InvalidPosixTime, code.Forbidden:
// 403
return http.StatusForbidden
case code.ResourceNotFound:
// 404
return http.StatusNotFound
case code.ResourceAlreadyExist, code.InvalidResourceState:
// 409
return http.StatusConflict
case code.NotValidImplementation:
// 501
return http.StatusNotImplemented
default:
}
// determine status code by category
switch e.Category() {
case code.CatInput:
return http.StatusBadRequest
default:
// return status code 500 if none of the condition is met
return http.StatusInternalServerError
}
}
// GeneralError transform category level error message
// It's the general error message for customer/API caller
func (e *Err) GeneralError() string {
if e == nil {
return ""
}
errStr, ok := code.CatToStr[e.Category()]
if !ok {
return ""
}
return errStr
}
// Is called when performing errors.Is().
// DO NOT USE THIS FUNCTION DIRECTLY unless you are very certain about what you're doing.
// Use errors.Is instead.
// This function compares if two error variables are both *Err, and have the same code (without checking the wrapped internal error)
func (e *Err) Is(f error) bool {
var err *Err
ok := errors.As(f, &err)
if !ok {
return false
}
return e.Code() == err.Code()
}
// Unwrap returns the underlying error
// The result of unwrapping an error may itself have an Unwrap method;
// we call the sequence of errors produced by repeated unwrapping the error chain.
func (e *Err) Unwrap() error {
if e == nil {
return nil
}
return e.internalErr
}
// Wrap sets the internal error to Err struct
func (e *Err) Wrap(internalErr error) *Err {
if e != nil {
e.internalErr = internalErr
}
return e
}
func (e *Err) GRPCStatus() *status.Status {
if e == nil {
return status.New(codes.OK, "")
}
return status.New(codes.Code(e.FullCode()), e.Error())
}