Files
IRC-kosmi-relay/bridge/kosmi/errors.go
cottongin dd398c9a8c sync
2025-11-01 21:00:16 -04:00

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
}