// gRPC status:上层判断「这次失败是不是超时」的入口。
// MoonBit 侧没有 Go 的 `codes` 包,这里把 P0 用得到的码定出来。
// 数值与 gRPC 规范一致,也与 `moonrpc` 的 `Status` 对齐。
///|
/// gRPC 状态码。
pub(all) enum Code {
Ok
Cancelled
Unknown
InvalidArgument
DeadlineExceeded
NotFound
AlreadyExists
PermissionDenied
ResourceExhausted
FailedPrecondition
Aborted
OutOfRange
Unimplemented
Internal
Unavailable
DataLoss
Unauthenticated
}
///|
impl Eq for Code with fn equal(self, other) {
self.to_int() == other.to_int()
}
///|
impl @debug.Debug for Code with fn to_repr(self) {
@debug.Repr::string(self.name())
}
///|
/// `Code` 的调试形态。`assert_eq` 的失败 diff 会用它,
/// 这里也开出来,便于别处打日志时拿到和断言一致的表示。
pub fn Code::debug_repr(self : Code) -> String {
@debug.to_string(self)
}
///|
pub fn Code::name(self : Code) -> String {
match self {
Ok => "OK"
Cancelled => "CANCELLED"
Unknown => "UNKNOWN"
InvalidArgument => "INVALID_ARGUMENT"
DeadlineExceeded => "DEADLINE_EXCEEDED"
NotFound => "NOT_FOUND"
AlreadyExists => "ALREADY_EXISTS"
PermissionDenied => "PERMISSION_DENIED"
ResourceExhausted => "RESOURCE_EXHAUSTED"
FailedPrecondition => "FAILED_PRECONDITION"
Aborted => "ABORTED"
OutOfRange => "OUT_OF_RANGE"
Unimplemented => "UNIMPLEMENTED"
Internal => "INTERNAL"
Unavailable => "UNAVAILABLE"
DataLoss => "DATA_LOSS"
Unauthenticated => "UNAUTHENTICATED"
}
}
///|
pub fn Code::to_int(self : Code) -> Int {
match self {
Ok => 0
Cancelled => 1
Unknown => 2
InvalidArgument => 3
DeadlineExceeded => 4
NotFound => 5
AlreadyExists => 6
PermissionDenied => 7
ResourceExhausted => 8
FailedPrecondition => 9
Aborted => 10
OutOfRange => 11
Unimplemented => 12
Internal => 13
Unavailable => 14
DataLoss => 15
Unauthenticated => 16
}
}
///|
/// 规范数值还原成枚举。未知数值归 `Unknown`;
/// 原始数值不丢,`RpcError::raw_code` 留着。
pub fn Code::from_int(n : Int) -> Code {
match n {
0 => Ok
1 => Cancelled
2 => Unknown
3 => InvalidArgument
4 => DeadlineExceeded
5 => NotFound
6 => AlreadyExists
7 => PermissionDenied
8 => ResourceExhausted
9 => FailedPrecondition
10 => Aborted
11 => OutOfRange
12 => Unimplemented
13 => Internal
14 => Unavailable
15 => DataLoss
16 => Unauthenticated
_ => Unknown
}
}
///|
/// 一次调用失败的全部信息。`raw_code` 保留线上原值,`code` 是归类后的结果。
///
/// 用 `suberror` 而不是 `struct`:调用侧要能 `raise` 它,
/// 否则「超时错误可被上层识别」就只剩比较返回值一条路。
pub suberror RpcError {
RpcError(Code, Int, String)
}
///|
/// 失败归类后的 gRPC 码。
pub fn RpcError::code(self : RpcError) -> Code {
let RpcError(code, _, _) = self
code
}
///|
/// 线上原始 `grpc-status` 数值。未知码也不丢。
pub fn RpcError::raw_code(self : RpcError) -> Int {
let RpcError(_, raw_code, _) = self
raw_code
}
///|
/// 服务端给的 `grpc-message`。
pub fn RpcError::message(self : RpcError) -> String {
let RpcError(_, _, message) = self
message
}
///|
/// 调用方看的一句话描述。
pub fn RpcError::to_string(self : RpcError) -> String {
let RpcError(code, _, message) = self
if message == "" {
code.name()
} else {
code.name() + ": " + message
}
}
///|
impl @debug.Debug for RpcError with fn to_repr(self) {
@debug.Repr::string(self.to_string())
}
///|
impl Show for RpcError with fn output(self, logger) {
logger.write_string(self.to_string())
}
///|
impl Eq for RpcError with fn equal(self, other) {
let RpcError(_, a_raw, a_msg) = self
let RpcError(_, b_raw, b_msg) = other
a_raw == b_raw && a_msg == b_msg
}
///|
/// 多个失败是否「同一类」。重试判断用得到:同一码、同一条消息即视为同类。
pub fn RpcError::same_kind(self : RpcError, other : RpcError) -> Bool {
self == other
}
///|
/// 把 gRPC 层的 (status, message) 包成上层能判别的错误。
pub fn RpcError::new(raw_code : Int, message : String) -> RpcError {
RpcError(Code::from_int(raw_code), raw_code, message)
}
///|
/// 是否可由调用方重试。读路径上 `Unavailable` 是典型可重试项;
/// 超时与参数错不盲目重试。
pub fn RpcError::is_retryable(self : RpcError) -> Bool {
match self.code() {
Unavailable | ResourceExhausted | Aborted => true
_ => false
}
}
///|
/// Issue 验收项「超时错误可被上层识别」落在这里:
/// 上层拿 `RpcError` 调它,而不是去比对魔法数字 4。
pub fn RpcError::is_deadline_exceeded(self : RpcError) -> Bool {
self.code() == DeadlineExceeded
}