storj/pkg/rpc/rpcstatus/status.go

178 lines
4.0 KiB
Go
Raw Normal View History

// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package rpcstatus
import (
"context"
"errors"
"fmt"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"storj.io/drpc/drpcerr"
)
// StatusCode is an enumeration of rpc status codes.
type StatusCode uint64
// These constants are all the rpc error codes. It is important that
// their numerical values do not change.
const (
Unknown StatusCode = iota
OK
Canceled
InvalidArgument
DeadlineExceeded
NotFound
AlreadyExists
PermissionDenied
ResourceExhausted
FailedPrecondition
Aborted
OutOfRange
Unimplemented
Internal
Unavailable
DataLoss
Unauthenticated
)
// Code returns the status code associated with the error.
func Code(err error) StatusCode {
// special case: if the error is context canceled or deadline exceeded, the code
// must be those. additionally, grpc returns OK for a nil error, so we will, too.
switch err {
case nil:
return OK
case context.Canceled:
return Canceled
case context.DeadlineExceeded:
return DeadlineExceeded
default:
if code := StatusCode(drpcerr.Code(err)); code != Unknown {
return code
}
if grpccode := status.Code(err); grpccode != codes.Unknown {
return statusCodeFromGRPC(grpccode)
}
return Unknown
}
}
// Error wraps the message with a status code into an error.
func Error(code StatusCode, msg string) error {
return &codeErr{
err: errors.New(msg),
code: code,
grpc: status.New(code.toGRPC(), msg),
}
}
// Errorf : Error :: fmt.Sprintf : fmt.Sprint
func Errorf(code StatusCode, format string, a ...interface{}) error {
return &codeErr{
err: fmt.Errorf(format, a...),
code: code,
grpc: status.Newf(code.toGRPC(), format, a...),
}
}
// codeErr implements error that can work both in grpc and drpc.
type codeErr struct {
err error
code StatusCode
grpc *status.Status
}
func (c *codeErr) Error() string { return c.err.Error() }
func (c *codeErr) Unwrap() error { return c.err }
func (c *codeErr) Cause() error { return c.err }
func (c *codeErr) Code() uint64 { return uint64(c.code) }
func (c *codeErr) GRPCStatus() *status.Status { return c.grpc }
// toGRPC returns the grpc version of the status code.
func (s StatusCode) toGRPC() codes.Code {
switch s {
case Unknown:
return codes.Unknown
case OK:
return codes.OK
case Canceled:
return codes.Canceled
case InvalidArgument:
return codes.InvalidArgument
case DeadlineExceeded:
return codes.DeadlineExceeded
case NotFound:
return codes.NotFound
case AlreadyExists:
return codes.AlreadyExists
case PermissionDenied:
return codes.PermissionDenied
case ResourceExhausted:
return codes.ResourceExhausted
case FailedPrecondition:
return codes.FailedPrecondition
case Aborted:
return codes.Aborted
case OutOfRange:
return codes.OutOfRange
case Unimplemented:
return codes.Unimplemented
case Internal:
return codes.Internal
case Unavailable:
return codes.Unavailable
case DataLoss:
return codes.DataLoss
case Unauthenticated:
return codes.Unauthenticated
default:
return codes.Unknown
}
}
// statusCodeFromGRPC turns the grpc status code into a StatusCode.
func statusCodeFromGRPC(code codes.Code) StatusCode {
switch code {
case codes.Unknown:
return Unknown
case codes.OK:
return OK
case codes.Canceled:
return Canceled
case codes.InvalidArgument:
return InvalidArgument
case codes.DeadlineExceeded:
return DeadlineExceeded
case codes.NotFound:
return NotFound
case codes.AlreadyExists:
return AlreadyExists
case codes.PermissionDenied:
return PermissionDenied
case codes.ResourceExhausted:
return ResourceExhausted
case codes.FailedPrecondition:
return FailedPrecondition
case codes.Aborted:
return Aborted
case codes.OutOfRange:
return OutOfRange
case codes.Unimplemented:
return Unimplemented
case codes.Internal:
return Internal
case codes.Unavailable:
return Unavailable
case codes.DataLoss:
return DataLoss
case codes.Unauthenticated:
return Unauthenticated
default:
return Unknown
}
}