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.
117 lines
2.9 KiB
117 lines
2.9 KiB
package errors
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
httpstatus "github.com/go-kratos/kratos/v2/transport/http/status"
|
|
|
|
"google.golang.org/genproto/googleapis/rpc/errdetails"
|
|
"google.golang.org/grpc/status"
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
const (
|
|
// UnknownCode is unknown code for error info.
|
|
UnknownCode = 500
|
|
// UnknownReason is unknown reason for error info.
|
|
UnknownReason = ""
|
|
// SupportPackageIsVersion1 this constant should not be referenced by any other code.
|
|
SupportPackageIsVersion1 = true
|
|
)
|
|
|
|
//go:generate protoc -I. --go_out=paths=source_relative:. errors.proto
|
|
|
|
func (e *Error) Error() string {
|
|
return fmt.Sprintf("error: code = %d reason = %s message = %s metadata = %v", e.Code, e.Reason, e.Message, e.Metadata)
|
|
}
|
|
|
|
// GRPCStatus returns the Status represented by se.
|
|
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
|
|
}
|
|
|
|
// Is matches each error in the chain with the target value.
|
|
func (e *Error) Is(err error) bool {
|
|
if se := new(Error); errors.As(err, &se) {
|
|
return se.Reason == e.Reason
|
|
}
|
|
return false
|
|
}
|
|
|
|
// WithMetadata with an MD formed by the mapping of key, value.
|
|
func (e *Error) WithMetadata(md map[string]string) *Error {
|
|
err := proto.Clone(e).(*Error)
|
|
err.Metadata = md
|
|
return err
|
|
}
|
|
|
|
// New returns an error object for the code, message.
|
|
func New(code int, reason, message string) *Error {
|
|
return &Error{
|
|
Code: int32(code),
|
|
Message: message,
|
|
Reason: reason,
|
|
}
|
|
}
|
|
|
|
// Newf New(code fmt.Sprintf(format, a...))
|
|
func Newf(code int, reason, format string, a ...interface{}) *Error {
|
|
return New(code, reason, fmt.Sprintf(format, a...))
|
|
}
|
|
|
|
// Errorf returns an error object for the code, message and error info.
|
|
func Errorf(code int, reason, format string, a ...interface{}) error {
|
|
return New(code, reason, fmt.Sprintf(format, a...))
|
|
}
|
|
|
|
// Code returns the http code for a error.
|
|
// It supports wrapped errors.
|
|
func Code(err error) int {
|
|
if err == nil {
|
|
return 200 //nolint:gomnd
|
|
}
|
|
if se := FromError(err); se != nil {
|
|
return int(se.Code)
|
|
}
|
|
return UnknownCode
|
|
}
|
|
|
|
// Reason returns the reason for a particular error.
|
|
// It supports wrapped errors.
|
|
func Reason(err error) string {
|
|
if se := FromError(err); se != nil {
|
|
return se.Reason
|
|
}
|
|
return UnknownReason
|
|
}
|
|
|
|
// FromError try to convert an error to *Error.
|
|
// It supports wrapped errors.
|
|
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 {
|
|
for _, detail := range gs.Details() {
|
|
switch d := detail.(type) {
|
|
case *errdetails.ErrorInfo:
|
|
return New(
|
|
httpstatus.FromGRPCCode(gs.Code()),
|
|
d.Reason,
|
|
gs.Message(),
|
|
).WithMetadata(d.Metadata)
|
|
}
|
|
}
|
|
}
|
|
return New(UnknownCode, UnknownReason, err.Error())
|
|
}
|
|
|