# core/collision.py import os from core.paths import get_project_root class CollisionPolicy: ABORT = "abort" # comportamiento estricto actual BACKUP = "backup" # mueve el existente a backup/ y sigue RENAME = "rename" # añade sufijo _1, _2... hasta nombre libre def safe_backup_name(path: str) -> str: """ Devuelve una ruta sin colisión dentro de backup/. Añade sufijo _1, _2... si el nombre base ya existe. """ root = get_project_root() backup_dir = os.path.join(root, "backup") base = os.path.basename(path) name, ext = os.path.splitext(base) candidate = os.path.join(backup_dir, base) counter = 1 while os.path.exists(candidate): candidate = os.path.join(backup_dir, f"{name}_{counter}{ext}") counter += 1 return candidate def resolve_collision(target_path: str, policy: str = CollisionPolicy.ABORT) -> str: """ Comprueba si target_path existe y aplica la política: ABORT → lanza FileExistsError BACKUP → mueve el existente a backup/ y devuelve target_path RENAME → devuelve una ruta alternativa libre (target_1.cbz, ...) Devuelve la ruta segura donde escribir. """ if not os.path.exists(target_path): return target_path if policy == CollisionPolicy.ABORT: raise FileExistsError( f"El archivo destino ya existe y no se sobrescribirá: {target_path}" ) if policy == CollisionPolicy.BACKUP: from core.backup import move_to_backup move_to_backup(target_path) return target_path if policy == CollisionPolicy.RENAME: base, ext = os.path.splitext(target_path) counter = 1 candidate = f"{base}_{counter}{ext}" while os.path.exists(candidate): counter += 1 candidate = f"{base}_{counter}{ext}" return candidate raise ValueError(f"Política de colisión desconocida: {policy}")