156 lines
4.2 KiB
Go
156 lines
4.2 KiB
Go
package bkosmi
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
)
|
|
|
|
// Sentinel errors for common failure scenarios
|
|
var (
|
|
// ErrNotConnected indicates the WebSocket connection is not established
|
|
ErrNotConnected = errors.New("not connected to Kosmi")
|
|
|
|
// ErrAuthFailed indicates authentication with Kosmi failed
|
|
ErrAuthFailed = errors.New("authentication failed")
|
|
|
|
// ErrConnectionLost indicates the WebSocket connection was lost
|
|
ErrConnectionLost = errors.New("connection lost")
|
|
|
|
// ErrTokenExpired indicates the authentication token has expired
|
|
ErrTokenExpired = errors.New("token expired")
|
|
|
|
// ErrInvalidRoomID indicates the room ID format is invalid
|
|
ErrInvalidRoomID = errors.New("invalid room ID")
|
|
|
|
// ErrRoomNotFound indicates the specified room does not exist
|
|
ErrRoomNotFound = errors.New("room not found")
|
|
|
|
// ErrMessageSendFailed indicates a message could not be sent
|
|
ErrMessageSendFailed = errors.New("failed to send message")
|
|
|
|
// ErrSubscriptionFailed indicates subscription to room messages failed
|
|
ErrSubscriptionFailed = errors.New("failed to subscribe to messages")
|
|
|
|
// ErrJoinRoomFailed indicates joining the room failed
|
|
ErrJoinRoomFailed = errors.New("failed to join room")
|
|
|
|
// ErrConnectionTimeout indicates a connection timeout occurred
|
|
ErrConnectionTimeout = errors.New("connection timeout")
|
|
|
|
// ErrInvalidResponse indicates an unexpected response from the server
|
|
ErrInvalidResponse = errors.New("invalid response from server")
|
|
)
|
|
|
|
// ConnectionError represents a connection-related error with context
|
|
type ConnectionError struct {
|
|
Op string // Operation that failed (e.g., "dial", "handshake", "subscribe")
|
|
URL string // WebSocket URL
|
|
Err error // Underlying error
|
|
}
|
|
|
|
func (e *ConnectionError) Error() string {
|
|
return fmt.Sprintf("connection error during %s to %s: %v", e.Op, e.URL, e.Err)
|
|
}
|
|
|
|
func (e *ConnectionError) Unwrap() error {
|
|
return e.Err
|
|
}
|
|
|
|
// AuthError represents an authentication-related error with context
|
|
type AuthError struct {
|
|
Op string // Operation that failed (e.g., "login", "token_refresh", "anonymous_login")
|
|
Reason string // Human-readable reason
|
|
Err error // Underlying error
|
|
}
|
|
|
|
func (e *AuthError) Error() string {
|
|
if e.Err != nil {
|
|
return fmt.Sprintf("auth error during %s: %s (%v)", e.Op, e.Reason, e.Err)
|
|
}
|
|
return fmt.Sprintf("auth error during %s: %s", e.Op, e.Reason)
|
|
}
|
|
|
|
func (e *AuthError) Unwrap() error {
|
|
return e.Err
|
|
}
|
|
|
|
// MessageError represents a message-related error with context
|
|
type MessageError struct {
|
|
Op string // Operation that failed (e.g., "send", "receive", "parse")
|
|
RoomID string // Room ID
|
|
Message string // Message content (truncated if long)
|
|
Err error // Underlying error
|
|
}
|
|
|
|
func (e *MessageError) Error() string {
|
|
return fmt.Sprintf("message error during %s in room %s: %v", e.Op, e.RoomID, e.Err)
|
|
}
|
|
|
|
func (e *MessageError) Unwrap() error {
|
|
return e.Err
|
|
}
|
|
|
|
// RoomError represents a room-related error with context
|
|
type RoomError struct {
|
|
Op string // Operation that failed (e.g., "join", "leave", "subscribe")
|
|
RoomID string // Room ID
|
|
Err error // Underlying error
|
|
}
|
|
|
|
func (e *RoomError) Error() string {
|
|
return fmt.Sprintf("room error during %s for room %s: %v", e.Op, e.RoomID, e.Err)
|
|
}
|
|
|
|
func (e *RoomError) Unwrap() error {
|
|
return e.Err
|
|
}
|
|
|
|
// IsRetryable returns true if the error is transient and the operation should be retried
|
|
func IsRetryable(err error) bool {
|
|
if err == nil {
|
|
return false
|
|
}
|
|
|
|
// Check for known retryable errors
|
|
if errors.Is(err, ErrConnectionLost) ||
|
|
errors.Is(err, ErrConnectionTimeout) ||
|
|
errors.Is(err, ErrTokenExpired) {
|
|
return true
|
|
}
|
|
|
|
// Check for connection errors (usually retryable)
|
|
var connErr *ConnectionError
|
|
if errors.As(err, &connErr) {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// IsFatal returns true if the error is fatal and reconnection should not be attempted
|
|
func IsFatal(err error) bool {
|
|
if err == nil {
|
|
return false
|
|
}
|
|
|
|
// Check for known fatal errors
|
|
if errors.Is(err, ErrAuthFailed) ||
|
|
errors.Is(err, ErrInvalidRoomID) ||
|
|
errors.Is(err, ErrRoomNotFound) {
|
|
return true
|
|
}
|
|
|
|
// Check for auth errors with specific reasons
|
|
var authErr *AuthError
|
|
if errors.As(err, &authErr) {
|
|
// Invalid credentials are fatal
|
|
if authErr.Reason == "invalid credentials" ||
|
|
authErr.Reason == "account not found" {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|