"""Hiérarchie d'exceptions du validateur-config.
Chaque classe correspond à un cas d'échec RÉEL du pipeline. Aucune exception
générique ne doit être levée directement : toujours la sous-classe la plus
précise, pour permettre un traitement exhaustif côté main(), jamais un
`except` générique silencieux.
Distinction importante : une VIOLATION de schéma (champ manquant, type
incorrect, valeur hors liste autorisée) n'est PAS une exception — c'est le
résultat normal et attendu de la validation, collecté dans une liste et
rapporté. Les exceptions ci-dessous couvrent uniquement les cas où la
validation elle-même ne peut pas avoir lieu (fichiers absents, malformés).
"""
class ErreurValidateur(Exception):
"""Base commune — ne jamais lever directement, toujours une sous-classe."""
class ErreurCibleIntrouvable(ErreurValidateur):
"""Le fichier de config à valider n'existe pas."""
class ErreurCibleNonAccessible(ErreurValidateur):
"""Permission de lecture refusée sur le fichier de config."""
class ErreurFormatFichierNonSupporte(ErreurValidateur):
"""Extension du fichier de config hors .toml/.json — format non reconnu."""
class ErreurParsingConfig(ErreurValidateur):
"""Le fichier de config existe et est lisible mais son contenu n'est pas
du TOML/JSON valide (erreur de syntaxe réelle dans le fichier lui-même,
pas une violation de schéma)."""
def __init__(self, chemin: str, detail: str):
self.chemin = chemin
self.detail = detail
super().__init__(f"{chemin}: {detail}")
class ErreurSchemaIntrouvable(ErreurValidateur):
"""Le fichier de schéma déclaré n'existe pas."""
class ErreurSchemaInvalide(ErreurValidateur):
"""Le schéma lui-même est malformé : JSON invalide, champ obligatoire
absent (`type`), ou type déclaré inconnu du validateur — vérifié AVANT
toute validation de la config cible, jamais supposé correct."""
def __init__(self, detail: str):
self.detail = detail
super().__init__(f"Schéma invalide : {detail}")
class ErreurEcritureRapport(ErreurValidateur):
"""Le rapport de sortie n'a pas pu être écrit (--out)."""
class ErreurInterne(ErreurValidateur):
"""Dernier recours NOMMÉ pour tout cas non prévu ci-dessus — jamais un
`except Exception: pass` silencieux."""
def __init__(self, cause: Exception):
self.cause = cause
super().__init__(f"Erreur interne non catégorisée : {type(cause).__name__}: {cause}")