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
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 |
|
}
|
|
|