feat(cpp/core): añadir process_state_machine pure

This commit is contained in:
2026-04-28 23:52:37 +02:00
parent 9a543b7502
commit 86a1e12204
4 changed files with 155 additions and 0 deletions
@@ -0,0 +1,47 @@
#include "core/process_state_machine.h"
namespace fn_ui {
ProcessState process_transition(ProcessState s, ProcessEvent e) {
using S = ProcessState;
using E = ProcessEvent;
switch (s) {
case S::Idle:
// Trigger es una solicitud, el cambio real ocurre con Spawned.
if (e == E::Spawned) return S::Running;
return s;
case S::Running:
if (e == E::Finished) return S::Success;
if (e == E::Failed || e == E::Timeout) return S::Error;
return s;
case S::Success:
case S::Error:
if (e == E::Reset) return S::Idle;
return s;
}
return s;
}
const char* process_state_name(ProcessState s) {
switch (s) {
case ProcessState::Idle: return "Idle";
case ProcessState::Running: return "Running";
case ProcessState::Success: return "Success";
case ProcessState::Error: return "Error";
}
return "Unknown";
}
const char* process_event_name(ProcessEvent e) {
switch (e) {
case ProcessEvent::Trigger: return "Trigger";
case ProcessEvent::Spawned: return "Spawned";
case ProcessEvent::Finished: return "Finished";
case ProcessEvent::Failed: return "Failed";
case ProcessEvent::Timeout: return "Timeout";
case ProcessEvent::Reset: return "Reset";
}
return "Unknown";
}
} // namespace fn_ui
@@ -0,0 +1,36 @@
#pragma once
// process_state_machine — logica pura de transiciones de un proceso async
// (idle/running/success/error) a partir de eventos. Sin threads, sin I/O.
//
// Diseñada para que `process_runner` (impuro, threads + popen) delegue las
// transiciones aqui y la UI/CLI puedan razonar sobre el estado sin depender
// de la implementacion concreta del runner.
namespace fn_ui {
enum class ProcessState {
Idle,
Running,
Success,
Error,
};
enum class ProcessEvent {
Trigger, // se solicita lanzar (no cambia estado por si solo)
Spawned, // el proceso/task realmente arranco
Finished, // termino con exito
Failed, // termino con error
Timeout, // se cancelo por timeout (-> Error)
Reset, // limpiar a Idle (solo desde Success/Error)
};
// Pura: dado (state, event) devuelve el nuevo estado. Transiciones invalidas
// devuelven el mismo estado sin mutar.
ProcessState process_transition(ProcessState s, ProcessEvent e);
// Nombres legibles para logs/UI (sin alocar — punteros a literales staticos).
const char* process_state_name(ProcessState s);
const char* process_event_name(ProcessEvent e);
} // namespace fn_ui
@@ -0,0 +1,71 @@
---
name: process_state_machine
kind: function
lang: cpp
domain: core
version: "1.0.0"
purity: pure
signature: "fn_ui::ProcessState fn_ui::process_transition(fn_ui::ProcessState s, fn_ui::ProcessEvent e); const char* fn_ui::process_state_name(fn_ui::ProcessState); const char* fn_ui::process_event_name(fn_ui::ProcessEvent)"
description: "State machine puro para procesos async. Estados: Idle, Running, Success, Error. Eventos: Trigger, Spawned, Finished, Failed, Timeout, Reset. Transiciones invalidas devuelven el mismo estado sin mutar."
tags: [state_machine, process, async, runner, pure]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: []
tested: true
tests: ["process_transition: idle/spawned -> running", "process_transition: running -> success/error", "process_transition: reset from terminal", "process_transition: invalid events keep state", "process_state_name and process_event_name"]
test_file_path: "cpp/tests/test_process_state_machine.cpp"
file_path: "cpp/functions/core/process_state_machine.cpp"
params:
- name: s
desc: "Estado actual (Idle, Running, Success, Error)"
- name: e
desc: "Evento a aplicar (Trigger, Spawned, Finished, Failed, Timeout, Reset)"
output: "Nuevo ProcessState. Si la transicion (s, e) no esta definida, devuelve s sin cambios. process_state_name y process_event_name retornan punteros a literales staticos (no alocan)."
---
# process_state_machine
Las apps del registry usan `process_runner` (impuro: threads, mutex, popen)
para lanzar reindex, builds, deploys... Esta funcion extrae el contrato de
estados a una tabla pura que se puede testear, simular y compartir con CLIs
y bots sin depender del runner concreto.
## Tabla de transiciones
| Estado actual | Evento | Nuevo estado |
|---------------|------------|--------------|
| Idle | Spawned | Running |
| Idle | Trigger | Idle (la solicitud no cambia el estado por si sola — el runner debe Spawnar) |
| Running | Finished | Success |
| Running | Failed | Error |
| Running | Timeout | Error |
| Success | Reset | Idle |
| Error | Reset | Idle |
| cualquier otra combinacion | | mismo estado |
`Trigger` se mantiene como evento explicito para que el runner pueda emitir
"se pidio lanzar" antes de saber si el proceso arranca (util para logs y UI).
## API
```cpp
namespace fn_ui {
enum class ProcessState { Idle, Running, Success, Error };
enum class ProcessEvent { Trigger, Spawned, Finished, Failed, Timeout, Reset };
ProcessState process_transition(ProcessState s, ProcessEvent e);
const char* process_state_name(ProcessState s);
const char* process_event_name(ProcessEvent e);
}
```
## Uso desde process_runner
`process_runner` mantiene su `std::atomic<int>` con el estado, pero llama a
`process_transition` para decidir el siguiente valor. Esto evita que la
logica de transicion se duplique entre el thread productor y la UI consumer.