From 76a7674dae069e0be15bed6af8450ad214f8dde2 Mon Sep 17 00:00:00 2001 From: Keuin Date: Sun, 2 Jul 2023 16:12:13 +0800 Subject: Refactor: distinct task errors with type enum. --- bilibili/errors/taskerror.go | 146 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 bilibili/errors/taskerror.go (limited to 'bilibili/errors/taskerror.go') diff --git a/bilibili/errors/taskerror.go b/bilibili/errors/taskerror.go new file mode 100644 index 0000000..644a794 --- /dev/null +++ b/bilibili/errors/taskerror.go @@ -0,0 +1,146 @@ +package errors + +import ( + "fmt" + "github.com/samber/lo" + "strings" +) + +type Type int + +const ( + // GetRoomInfo means failed to read live room information + GetRoomInfo Type = iota + // GetLiveInfo means failed to get live information + GetLiveInfo + // StreamCopy means an error occurred while reading stream video data + StreamCopy + // LiveEnded means the live is ended + LiveEnded + // DanmakuServerConnection means failed to connect to danmaku server, which provides danmaku and other control data + DanmakuServerConnection + // Heartbeat means an error occurred while sending heartbeat message, which may indicate a broken network connection + Heartbeat + // InitialLiveStatus means failed to get the live status for the first time, which happens in the early stage + InitialLiveStatus + // DanmakuExchangeRead means an error occurred while reading danmaku datagram from the server, + // which may indicate a broken network connection + DanmakuExchangeRead + // GetDanmakuServerInfo means failed to get danmaku server info + GetDanmakuServerInfo + // RecoverLiveStatusChecker means failed to restart live status checker + RecoverLiveStatusChecker + + // FileCreation means failed to create a file + FileCreation + // InvalidLiveInfo means the live info is insufficient for recording + InvalidLiveInfo + // LiveStatusWatch means the live status watcher encountered an unrecoverable error + LiveStatusWatch + // Unknown means the error type is unexpected and is not suitable to retry for safety + Unknown + // InvalidAuthProtocol means authentication failed because the protocol is invalid, + // which may indicate the protocol implementation is outdated + InvalidAuthProtocol + // MessageDecompression means the message cannot be decompressed, and we cannot understand its content + MessageDecompression + // JsonDecode means we cannot decode a datum which is expected to be a JSON object string + JsonDecode +) + +var recoverableErrors = []Type{ + GetRoomInfo, + GetLiveInfo, + StreamCopy, + LiveEnded, + DanmakuServerConnection, + Heartbeat, + InitialLiveStatus, + DanmakuExchangeRead, + GetDanmakuServerInfo, + RecoverLiveStatusChecker, +} + +var errorStrings = map[Type]string{ + GetRoomInfo: "failed to get living room information", + GetLiveInfo: "failed to get live info", + StreamCopy: "stream copy was unexpectedly interrupted", + LiveEnded: "live is ended", + DanmakuServerConnection: "failed to connect to danmaku server", + Heartbeat: "heartbeat failed", + InitialLiveStatus: "check initial live status failed", + DanmakuExchangeRead: "failed to read exchange from server", + GetDanmakuServerInfo: "cannot get notification server info", + RecoverLiveStatusChecker: "when recovering from a previous error, another error occurred", + FileCreation: "failed to create file", + InvalidLiveInfo: "invalid live info", + LiveStatusWatch: "failed to watch live status", + Unknown: "unexpected error type", + InvalidAuthProtocol: "authentication failed, invalid protocol", + MessageDecompression: "failed to decompress server message", + JsonDecode: "invalid JSON response from server", +} + +func (t Type) String() string { + if s, ok := errorStrings[t]; ok { + return s + } + return fmt.Sprintf("", int(t)) +} + +type taskError struct { + typ Type + err []error +} + +func (e *taskError) Message() string { + //TODO implement me + panic("implement me") +} + +func (e *taskError) IsRecoverable() bool { + return lo.Contains(recoverableErrors, e.typ) +} + +func (e *taskError) Unwrap() []error { + return e.err +} + +func (e taskError) Error() string { + sb := strings.Builder{} + if e.IsRecoverable() { + sb.WriteString("recoverable task error") + } else { + sb.WriteString("unrecoverable task error") + } + sb.WriteString(": ") + sb.WriteString(fmt.Sprintf("%v", e.typ)) + if len(e.err) > 0 { + sb.WriteString(", ") + for i := range e.err { + sb.WriteString(fmt.Sprintf("%v", e.err[i])) + if i != len(e.err)-1 { + sb.WriteString(", ") + } + } + } + return sb.String() +} + +type TaskError interface { + // IsRecoverable reports if this task error is safe to retry. + IsRecoverable() bool + // Unwrap returns the underneath errors which cause the task error. + Unwrap() []error + // Message returns the detailed task-specific error message. + Message() string + // Error returns this error as string + Error() string +} + +func NewError(typ Type, err ...error) TaskError { + return &taskError{ + typ: typ, + err: err, + } +} -- cgit v1.2.3