"""Mapper from exceptions to HTTP responses is defined here""" from typing import Callable, Type from fastapi.responses import JSONResponse from starlette.exceptions import HTTPException class ExceptionMapper: """Maps exceptions to `JSONResponse` for FastAPI error handling.""" def __init__(self): self._known_exceptions: dict[Type, Callable[[Exception], JSONResponse]] = {} def register_simple(self, exception_type: Type, status_code: int, details: str) -> None: """Register simple response handler with setting status_code and details.""" self._known_exceptions[exception_type] = lambda _: JSONResponse( {"error": f"{exception_type.__module__}.{exception_type.__qualname__}", "details": details}, status_code=status_code, ) def register_func(self, exception_type: Type, func: Callable[[Exception], JSONResponse]) -> None: """Register complex response handler by passing function.""" self._known_exceptions[exception_type] = func def get_status_code(self, exc: Exception) -> int: """Get status code of preparing response.""" if isinstance(exc, HTTPException): return exc.status_code if type(exc) in self._known_exceptions: return self._known_exceptions[type(exc)](exc).status_code return 500 def is_known(self, exc: Exception) -> bool: return type(exc) in self._known_exceptions or isinstance(exc, HTTPException) def apply(self, exc: Exception) -> JSONResponse: if (res := self.apply_if_known(exc)) is not None: return res return JSONResponse({"code": 500, "detail": "unhandled exception"}, status_code=500) def apply_if_known(self, exc: Exception) -> JSONResponse | None: if isinstance(exc, HTTPException): return JSONResponse({"code": exc.status_code, "detail": exc.detail}, status_code=exc.status_code) if (t := type(exc)) in self._known_exceptions: return self._known_exceptions[t](exc) return None