library-go/errors/errors.go

219 lines
4.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package error
import (
code2 "code.30cm.net/wanderland/library-go/errors/code"
"errors"
"fmt"
"net/http"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// Scope 全域變數應由服務或模組設置
var Scope = code2.Unset
// Err 6 碼,服務 2, 大類 2, 詳細錯誤 2
type Err struct {
category uint32
code uint32
scope uint32
msg string
internalErr error
}
// Error 是錯誤的介面
// 私有屬性 "msg" 的 getter 函數
func (e *Err) Error() string {
if e == nil {
return ""
}
// 如果 internal err 存在,連接錯誤字串
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 私有屬性 "category" 的 getter 函數
func (e *Err) Category() uint32 {
if e == nil {
return 0
}
return e.category
}
// Scope 私有屬性 "scope" 的 getter 函數
func (e *Err) Scope() uint32 {
if e == nil {
return code2.Unset
}
return e.scope
}
// CodeStr 返回帶有零填充的錯誤代碼字串
func (e *Err) CodeStr() string {
if e == nil {
return "00000"
}
if e.Category() == code2.CatGRPC {
return fmt.Sprintf("%d%04d", e.Scope(), e.Category()+e.Code())
}
return fmt.Sprintf("%d%04d", e.Scope(), e.Code())
}
// Code 私有屬性 "code" 的 getter 函數
func (e *Err) Code() uint32 {
if e == nil {
return code2.OK
}
return e.code
}
func (e *Err) FullCode() uint32 {
if e == nil {
return 0
}
if e.Category() == code2.CatGRPC {
return e.Scope()*10000 + e.Category() + e.Code()
}
return e.Scope()*10000 + e.Code()
}
// HTTPStatus 返回對應的 HTTP 狀態碼
func (e *Err) HTTPStatus() int {
if e == nil || e.Code() == code2.OK {
return http.StatusOK
}
// 根據 code 判斷狀態碼
switch e.Code() {
case code2.ResourceInsufficient:
// 400
return http.StatusBadRequest
case code2.Unauthorized, code2.InsufficientPermission:
// 401
return http.StatusUnauthorized
case code2.InsufficientQuota:
// 402
return http.StatusPaymentRequired
case code2.InvalidPosixTime, code2.Forbidden:
// 403
return http.StatusForbidden
case code2.ResourceNotFound:
// 404
return http.StatusNotFound
case code2.ResourceAlreadyExist, code2.InvalidResourceState:
// 409
return http.StatusConflict
case code2.NotValidImplementation:
// 501
return http.StatusNotImplemented
default:
}
// 根據 category 判斷狀態碼
switch e.Category() {
case code2.CatInput:
return http.StatusBadRequest
default:
// 如果沒有符合的條件,返回狀態碼 500
return http.StatusInternalServerError
}
}
// GeneralError 轉換 category 級別錯誤訊息
// 這是給客戶或 API 調用者的一般錯誤訊息
func (e *Err) GeneralError() string {
if e == nil {
return ""
}
errStr, ok := code2.CatToStr[e.Category()]
if !ok {
return ""
}
return errStr
}
// Is 在執行 errors.Is() 時調用。
// 除非你非常確定你在做什麼,否則不要直接使用這個函數。
// 請使用 errors.Is 代替。
// 此函數比較兩個錯誤變量是否都是 *Err並且具有相同的 code不檢查包裹的內部錯誤
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 返回底層錯誤
// 解除包裹錯誤的結果本身可能具有 Unwrap 方法;
// 我們稱通過反覆解除包裹產生的錯誤序列為錯誤鏈。
func (e *Err) Unwrap() error {
if e == nil {
return nil
}
return e.internalErr
}
// Wrap 將內部錯誤設置到 Err 結構
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())
}
// 工廠函數
// NewErr 創建新的 Err
func NewErr(scope, category, detail uint32, msg string) *Err {
return &Err{
category: category,
code: detail,
scope: scope,
msg: msg,
}
}
// NewGRPCErr 創建新的 gRPC Err
func NewGRPCErr(scope, detail uint32, msg string) *Err {
return &Err{
category: code2.CatGRPC,
code: detail,
scope: scope,
msg: msg,
}
}