summaryrefslogtreecommitdiff
path: root/bilibili/errors/taskerror.go
blob: 644a7941f8684593386441d2719266789354d593 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
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("<Type %v>", 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,
	}
}