61 lines
1.9 KiB
Python
61 lines
1.9 KiB
Python
# 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}")
|