"""CSV input adapters. The service depends on the protocol, not pandas I/O directly.""" from __future__ import annotations from io import BytesIO from pathlib import Path from typing import Protocol import pandas as pd from .errors import InputDataError class FrameReader(Protocol): def read_bytes(self, payload: bytes) -> pd.DataFrame: ... def read_path(self, path: Path) -> pd.DataFrame: ... class PandasCsvReader: def __init__(self, max_bytes: int) -> None: self.max_bytes = max_bytes def read_bytes(self, payload: bytes) -> pd.DataFrame: if not payload: raise InputDataError("Przesłany plik jest pusty.", code="empty_file") if len(payload) > self.max_bytes: limit_mb = self.max_bytes / (1024 * 1024) raise InputDataError( f"Plik przekracza limit {limit_mb:.0f} MB.", hint="Podziel dane na mniejsze kompletne zestawy silników.", code="file_too_large", ) try: return pd.read_csv(BytesIO(payload)).reset_index(drop=True) except UnicodeDecodeError as exc: raise InputDataError( "Plik nie jest poprawnym tekstowym CSV w kodowaniu UTF-8.", hint="Zapisz plik jako CSV UTF-8 i spróbuj ponownie.", code="invalid_encoding", ) from exc except (pd.errors.EmptyDataError, pd.errors.ParserError) as exc: raise InputDataError( "Nie udało się odczytać struktury CSV.", hint="Sprawdź separator, nagłówek oraz liczbę kolumn w każdym wierszu.", code="malformed_csv", ) from exc def read_path(self, path: Path) -> pd.DataFrame: try: payload = path.read_bytes() except OSError as exc: raise InputDataError( f"Nie można odczytać pliku {path.name!r}.", hint="Sprawdź, czy plik istnieje i ma uprawnienia do odczytu.", code="file_unavailable", ) from exc return self.read_bytes(payload)