"""Typed failures raised by the Spotify adapter.""" from app.ports.protocols import CatalogQuotaExhaustedError class SpotifyError(Exception): """Base class for Spotify adapter failures.""" class SpotifyAuthenticationError(SpotifyError): """Spotify rejected authentication or token refresh.""" class SpotifyRateLimitedError(SpotifyError): """Spotify rate limited a request that could not be retried.""" def __init__(self, retry_after_seconds: float | None, reason: str | None = None) -> None: """Record Spotify's requested delay without exposing request data.""" super().__init__("Spotify rate limit exceeded") self.retry_after_seconds = retry_after_seconds self.reason = reason class SpotifyQuotaExhaustedError(SpotifyRateLimitedError, CatalogQuotaExhaustedError): """Spotify rejected a request because the application quota is exhausted.""" class SpotifyUnavailableError(SpotifyError): """Spotify returned a server-side failure.""" def __init__(self, status_code: int, message: str | None = None) -> None: """Record safe details from a Spotify server failure.""" generic_message = f"Spotify is unavailable with status {status_code}" super().__init__(message if message is not None else generic_message) self.status_code = status_code self.message = message class SpotifyRequestError(SpotifyError): """Spotify rejected a non-authenticated API request.""" def __init__(self, status_code: int, message: str | None = None) -> None: """Record safe details from a rejected Spotify request.""" generic_message = f"Spotify request failed with status {status_code}" super().__init__(message if message is not None else generic_message) self.status_code = status_code self.message = message