You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

121 lines
2.6 KiB

package errors
import (
"errors"
"fmt"
httpstatus "git.diulo.com/mogfee/kit/transport/http/status"
"google.golang.org/genproto/googleapis/rpc/errdetails"
"google.golang.org/grpc/status"
)
const (
// UnknownCode is unknown code for error info.
UnknownCode = 500
// UnknownReason is unknown reason for error info.
UnknownReason = ""
)
type Error struct {
Status
cause error
}
func (e *Error) Error() string {
return fmt.Sprintf("error: code = %d reason = %s message = %s metadata = %v cause = %v", e.Code, e.Reason, e.Message, e.Metadata, e.cause)
}
func (e *Error) Unwarp() error {
return e.cause
}
func (e *Error) Is(err error) bool {
if se := new(Error); errors.As(err, &se) {
return se.Code == e.Code && se.Reason == e.Reason
}
return false
}
func (e *Error) WithCause(cause error) *Error {
err := Clone(e)
err.cause = cause
return err
}
func (e *Error) WithMetadata(md map[string]string) *Error {
err := Clone(e)
err.Metadata = md
return err
}
func (e *Error) GRPCStatus() *status.Status {
s, _ := status.New(httpstatus.ToGRPCCode(int(e.Code)), e.Message).
WithDetails(&errdetails.ErrorInfo{
Reason: e.Reason,
Metadata: e.Metadata,
})
return s
}
func New(code int, reason, message string) *Error {
return &Error{
Status: Status{
Code: int32(code),
Reason: reason,
Message: message,
},
}
}
func Newf(code int, reason string, format string, a ...any) *Error {
return New(code, reason, fmt.Sprintf(format, a...))
}
func Errorf(code int, reason string, format string, a ...any) *Error {
return New(code, reason, fmt.Sprintf(format, a...))
}
func Code(err error) int {
if err == nil {
return 200
}
return int(FromError(err).Code)
}
func Reason(err error) string {
if err == nil {
return UnknownReason
}
return FromError(err).Reason
}
func Clone(err *Error) *Error {
if err == nil {
return nil
}
metadata := make(map[string]string, len(err.Metadata))
for k, v := range err.Metadata {
metadata[k] = v
}
return &Error{
cause: err.cause,
Status: Status{
Code: err.Code,
Reason: err.Reason,
Message: err.Message,
Metadata: metadata,
},
}
}
func FromError(err error) *Error {
if err == nil {
return nil
}
if se := new(Error); errors.As(err, &se) {
return se
}
gs, ok := status.FromError(err)
if !ok {
return New(UnknownCode, UnknownReason, err.Error())
}
ret := New(httpstatus.FromGRPCCode(gs.Code()), UnknownReason, gs.Message())
for _, detail := range gs.Details() {
switch d := detail.(type) {
case *errdetails.ErrorInfo:
ret.Reason = d.Reason
return ret.WithMetadata(d.Metadata)
}
}
return ret
}