from __future__ import annotations import shutil from pathlib import Path import anyio class LocalStorageAdapter: def __init__(self, base_path: str) -> None: self.base_path = Path(base_path) async def ensure_base_path(self) -> Path: await anyio.to_thread.run_sync(lambda: self.base_path.mkdir(parents=True, exist_ok=True)) return self.base_path async def check(self) -> dict[str, str]: path = await self.ensure_base_path() is_dir = await anyio.to_thread.run_sync(path.is_dir) if not is_dir: raise NotADirectoryError(str(path)) return {"status": "healthy", "detail": str(path)} async def put_file(self, source_path: str, storage_key: str) -> dict[str, str | None]: destination = self.base_path / storage_key await anyio.to_thread.run_sync(lambda: destination.parent.mkdir(parents=True, exist_ok=True)) def _copy_if_needed() -> None: if not destination.exists(): shutil.copyfile(source_path, destination) await anyio.to_thread.run_sync(_copy_if_needed) return {"etag": None, "storage_key": storage_key} async def download_file(self, storage_key: str, destination_path: Path) -> Path: source = await self.resolve_path(storage_key) await anyio.to_thread.run_sync(lambda: destination_path.parent.mkdir(parents=True, exist_ok=True)) await anyio.to_thread.run_sync(lambda: shutil.copyfile(source, destination_path)) return destination_path async def resolve_path(self, storage_key: str) -> Path: destination = self.base_path / storage_key exists = await anyio.to_thread.run_sync(destination.exists) if not exists: raise FileNotFoundError(storage_key) return destination async def object_exists(self, storage_key: str) -> bool: destination = self.base_path / storage_key return await anyio.to_thread.run_sync(destination.exists)