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 }