library-go/errors/errors.go

219 lines
4.4 KiB
Go
Raw Normal View History

2024-08-06 09:38:33 +00:00
package error
import (
2024-08-07 16:01:58 +00:00
code2 "code.30cm.net/wanderland/library-go/errors/code"
2024-08-06 09:38:33 +00:00
"errors"
"fmt"
"net/http"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// Scope 全域變數應由服務或模組設置
2024-08-07 16:01:58 +00:00
var Scope = code2.Unset
2024-08-06 09:38:33 +00:00
2024-08-06 16:23:40 +00:00
// Err 6 碼,服務 2, 大類 2, 詳細錯誤 2
2024-08-06 09:38:33 +00:00
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 {
2024-08-07 16:01:58 +00:00
return code2.Unset
2024-08-06 09:38:33 +00:00
}
return e.scope
}
// CodeStr 返回帶有零填充的錯誤代碼字串
func (e *Err) CodeStr() string {
if e == nil {
return "00000"
}
2024-08-07 16:01:58 +00:00
if e.Category() == code2.CatGRPC {
2024-08-06 09:38:33 +00:00
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 {
2024-08-07 16:01:58 +00:00
return code2.OK
2024-08-06 09:38:33 +00:00
}
return e.code
}
func (e *Err) FullCode() uint32 {
if e == nil {
return 0
}
2024-08-07 16:01:58 +00:00
if e.Category() == code2.CatGRPC {
2024-08-06 09:38:33 +00:00
return e.Scope()*10000 + e.Category() + e.Code()
}
return e.Scope()*10000 + e.Code()
}
// HTTPStatus 返回對應的 HTTP 狀態碼
func (e *Err) HTTPStatus() int {
2024-08-07 16:01:58 +00:00
if e == nil || e.Code() == code2.OK {
2024-08-06 09:38:33 +00:00
return http.StatusOK
}
// 根據 code 判斷狀態碼
switch e.Code() {
2024-08-07 16:01:58 +00:00
case code2.ResourceInsufficient:
2024-08-06 09:38:33 +00:00
// 400
return http.StatusBadRequest
2024-08-07 16:01:58 +00:00
case code2.Unauthorized, code2.InsufficientPermission:
2024-08-06 09:38:33 +00:00
// 401
return http.StatusUnauthorized
2024-08-07 16:01:58 +00:00
case code2.InsufficientQuota:
2024-08-06 09:38:33 +00:00
// 402
return http.StatusPaymentRequired
2024-08-07 16:01:58 +00:00
case code2.InvalidPosixTime, code2.Forbidden:
2024-08-06 09:38:33 +00:00
// 403
return http.StatusForbidden
2024-08-07 16:01:58 +00:00
case code2.ResourceNotFound:
2024-08-06 09:38:33 +00:00
// 404
return http.StatusNotFound
2024-08-07 16:01:58 +00:00
case code2.ResourceAlreadyExist, code2.InvalidResourceState:
2024-08-06 09:38:33 +00:00
// 409
return http.StatusConflict
2024-08-07 16:01:58 +00:00
case code2.NotValidImplementation:
2024-08-06 09:38:33 +00:00
// 501
return http.StatusNotImplemented
default:
}
// 根據 category 判斷狀態碼
switch e.Category() {
2024-08-07 16:01:58 +00:00
case code2.CatInput:
2024-08-06 09:38:33 +00:00
return http.StatusBadRequest
default:
// 如果沒有符合的條件,返回狀態碼 500
return http.StatusInternalServerError
}
}
// GeneralError 轉換 category 級別錯誤訊息
// 這是給客戶或 API 調用者的一般錯誤訊息
func (e *Err) GeneralError() string {
if e == nil {
return ""
}
2024-08-07 16:01:58 +00:00
errStr, ok := code2.CatToStr[e.Category()]
2024-08-06 09:38:33 +00:00
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())
}
2024-08-07 08:27:15 +00:00
// 工廠函數
// 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{
2024-08-07 16:01:58 +00:00
category: code2.CatGRPC,
2024-08-07 08:27:15 +00:00
code: detail,
scope: scope,
msg: msg,
}
}