

# Exceptions Structure and Handling in FastF1
#
# FastF1 code can be categorized into two parts with regard to exception
# handling:
# - Interface code that is tightly coupled with the user-facing API. These
#   parts of the code usually have a short runtime and perform actions that
#   are very limited in scope. E.g., selecting a specific subset of data.
# - Data processing code that is very self-contained with limited user
#   interaction. While this code is triggered from the user-facing API, it
#   then performs a large range of data loading and processing operations
#   before returning to the user. When errors are encountered during this,
#   they should be handled as gracefully as possible. The user is only warned
#   about the problem and degraded data may be returned. This is preferred
#   over returning no data at all. Catch-all exception handling is used to
#   ensure that all unexpected errors are handled in some way. The errors are
#   transformed into warnings instead.
#
# In some special cases, errors during data processing may be unrecoverable,
# or we don't want to recover them. For example, when API rate limits are
# exceeded. Therefore, the FastF1CriticalException subclass exists as a base
# class for all exceptions that are generated by FastF1 and that need to break
# through the catch-all error handling.


# ### Default FastF1 Exceptions ###

class DataNotLoadedError(Exception):
    """Raised if an attempt is made to access data that has not been loaded
    yet."""
    pass


class ErgastError(Exception):
    """Base class for Ergast API errors."""
    pass


class ErgastJsonError(ErgastError):
    """The response returned by the server could not be parsed."""
    pass


class ErgastInvalidRequestError(ErgastError):
    """The server rejected the request because it was invalid."""
    pass


class InvalidSessionError(Exception):
    """Raised if no session for the specified event name, type, and year
    can be found."""

    def __init__(self, *args):
        super().__init__("No matching session can be found.")


class NoLapDataError(Exception):
    """
    Raised if the API request does not fail, but there is no usable data
    after processing the result.
    """
    def __init__(self, *args):
        super().__init__("Failed to load session because the API did not "
                         "provide any usable data.")


# ### Critical FastF1 Exceptions ###

class FastF1CriticalError(RuntimeError):
    """Base class for unrecoverable exceptions that occur during internal
    data processing. These exceptions are always raised to the user and data
    processing is terminated.
    """
    # Exceptions deriving from this base class must only be used in data
    # processing code. They must never be handled by FastF1.
    pass


class RateLimitExceededError(FastF1CriticalError):
    """Raised if a hard rate limit is exceeded for any API."""
    pass
