diff --git a/cpp/apps/primitives_gallery/CMakeLists.txt b/cpp/apps/primitives_gallery/CMakeLists.txt index 4f8bc6ba..5e542906 100644 --- a/cpp/apps/primitives_gallery/CMakeLists.txt +++ b/cpp/apps/primitives_gallery/CMakeLists.txt @@ -39,6 +39,7 @@ add_imgui_app(primitives_gallery ${CMAKE_SOURCE_DIR}/functions/core/toast.cpp ${CMAKE_SOURCE_DIR}/functions/core/tree_view.cpp ${CMAKE_SOURCE_DIR}/functions/core/process_runner.cpp + ${CMAKE_SOURCE_DIR}/functions/core/process_state_machine.cpp # Viz primitives demoed ${CMAKE_SOURCE_DIR}/functions/viz/kpi_card.cpp ${CMAKE_SOURCE_DIR}/functions/viz/bar_chart.cpp diff --git a/cpp/functions/core/process_state_machine.cpp b/cpp/functions/core/process_state_machine.cpp new file mode 100644 index 00000000..6115bba9 --- /dev/null +++ b/cpp/functions/core/process_state_machine.cpp @@ -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 diff --git a/cpp/functions/core/process_state_machine.h b/cpp/functions/core/process_state_machine.h new file mode 100644 index 00000000..e6da9c90 --- /dev/null +++ b/cpp/functions/core/process_state_machine.h @@ -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 diff --git a/cpp/functions/core/process_state_machine.md b/cpp/functions/core/process_state_machine.md new file mode 100644 index 00000000..86450842 --- /dev/null +++ b/cpp/functions/core/process_state_machine.md @@ -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` 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.