02.03.02 — La machine à états du builder#
Fichier source : src/ocarina/dsl/testing_with_railway/internals/action_chain.py
C’est ici qu’est implémentée toute la mécanique ROP : le passage par quatre états successifs typés, qui interdit littéralement (au sens du type-checker) toute fantaisie syntaxique. Le DSL est aussi son propre système immunitaire.
Vue d’ensemble#
┌─────────────────┐
start(action) → │ ActionStart[T] │ un Action[T] = Thunk[Result[T]]
└────────┬────────┘
│ .failure(failure_handler)
▼
┌─────────────────┐
│ ActionFailure[T]│
└────────┬────────┘
│ .success(success_handler)
▼
┌─────────────────┐
│ ActionSuccess[T]│
└────────┬────────┘
│ .execute() ── exécute action(), fire le handler
▼
┌─────────────────┐
│ ActionChain[T] │ contient (has_failed, result)
└──────┬──────────┘
│ .then(next_action_or_start)
┌──────────────┴──────────────┐
│ │
has_failed=True has_failed=False
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ NeutralAction- │ │ ActionStart[T] │ → cycle recommence
│ Start[T] │ │ (l'action suiv. │
│ (rail d'échec : │ │ sera exécutée) │
│ toute la chaîne │ └──────────────────┘
│ devient no-op, │
│ tout en gardant │
│ l'API fluide) │
└──────────────────┘
│
▼ (.failure → .success → .execute, tout en no-op)
┌──────────────────┐
│ ActionChain[T] │ has_failed=True, result=<le Fail accumulé>
└──────────────────┘
Les types Action, FailureHandler, SuccHandler#
type Action[T] = Thunk[Result[T]] # () -> Result[T]
type FailureHandler = Callable[[Exception], None]
type SuccHandler = Effect # () -> None
Action[T] est un Thunk. Cela veut dire qu’au moment où ActionStart(action) est appelé, rien n’est exécuté. L’action est juste capturée.FailureHandler reçoit l’exception, mais ne reçoit pas le Fail ni le Result. C’est volontaire : 99% des handlers veulent tracer l’exception, prendre un screenshot, et c’est tout.SuccHandler est un Effect (sans argument). Idem : un handler de succès log un message statique, fait un screenshot, pas besoin du résultat.
Typestate Pattern : pourquoi on ne peut pas se tromper#
L’enchaînement est strictement linéaire. Chaque méthode renvoie un type différent, qui n’expose que la suite logique de l’enchaînement :