--- id: "0045" title: "Extraer logica pura de componentes C++ impuros (sql_workbench, process_runner, file_watcher)" status: completado type: refactor domain: - cpp-stack - registry-quality scope: registry-only priority: media depends: [] blocks: [] related: [] created: 2026-05-17 updated: 2026-05-17 tags: [] --- # 0045 — Extraer logica pura de componentes C++ impuros (sql_workbench, process_runner, file_watcher) ## Metadata | Campo | Valor | |-------|-------| | **ID** | 0045 | | **Estado** | pendiente | | **Prioridad** | media | | **Tipo** | refactor — `cpp/functions/core` | ## Dependencias Ninguna. --- ## Objetivo Extraer la parte pura (parsing, state machines, diff) de componentes hoy mezclados con I/O y UI. Resultado: cada uno se descompone en `_pure_cpp_core` (testeable, sin ImGui ni I/O) + `_ui_cpp_core` (thin wrapper impuro). Tambien aplica al compiler de `shaders_lab` que vive en su `main.cpp`. ## Contexto - `sql_workbench_cpp_core` mezcla parsing SQL, ejecucion SQLite y render ImGui en una sola funcion. - `process_runner_cpp_core` mezcla state machine de proceso (idle/running/success/error) con UI helpers. - `file_watcher_cpp_core` mezcla polling de filesystem con notificaciones UI. - `shaders_lab/main.cpp` tiene `compile_code()`, `compile_dag()`, `mark_code_dirty()` inline. Sin separacion no hay tests unitarios, no es reutilizable y crece la deuda con cada feature. ## Arquitectura ``` cpp/functions/core/ ├── sql_parse.{h,cpp,md} # NEW — pure ├── sql_workbench.{h,cpp,md} # MOD — thin wrapper, llama sql_parse ├── process_state_machine.{h,cpp,md} # NEW — pure (transiciones) ├── process_runner.{h,cpp,md} # MOD — usa process_state_machine ├── file_poll_diff.{h,cpp,md} # NEW — pure (calcula diff de snapshots) └── file_watcher.{h,cpp,md} # MOD — usa file_poll_diff cpp/apps/shaders_lab/ ├── compiler.h # NEW ├── compiler.cpp # NEW (extrae compile_code/compile_dag) └── main.cpp # MOD (461 → ~250 lineas) ``` ## Tareas ### Fase 1 — sql_workbench 1.1 `sql_parse_cpp_core` (pure): tokeniza SQL minimal (separa statements por `;`, detecta keyword inicial: SELECT/INSERT/UPDATE/DELETE/CREATE/etc., devuelve struct `SqlStatement{kind, text, line}`). 1.2 `sql_workbench` pasa a llamar `sql_parse` para detectar tipo de statement antes de ejecutar; el parser puro es testeable sin SQLite. 1.3 Test unitario: dado SQL multi-statement, devuelve N statements con kind correcto. ### Fase 2 — process_runner 2.1 `process_state_machine_cpp_core` (pure): funcion `process_transition(current_state, event) -> new_state` con eventos `{Trigger, Spawned, Finished, Failed, Timeout}` y estados `{Idle, Running, Success, Error}`. 2.2 `process_runner` pasa a usar la maquina pura para gestionar transiciones; la parte impura solo dispara `popen`/`spawn`/`wait` y traduce a eventos. 2.3 Test unitario: secuencia de eventos → estado final esperado. ### Fase 3 — file_watcher 3.1 `file_poll_diff_cpp_core` (pure): dado dos snapshots `vector`, devuelve `FileDiff{added, modified, removed}`. 3.2 `file_watcher` pasa a hacer el polling impuro y delegar el diff al puro. 3.3 Test unitario: dado A=[a,b,c], B=[a,b',d] → modified=[b], added=[d], removed=[c]. ### Fase 4 — shaders_lab compiler 4.1 Crear `cpp/apps/shaders_lab/compiler.{h,cpp}` con `compile_code(code, &out_log) -> bool`, `compile_dag(...)`, `mark_code_dirty(...)`. Estas funciones llaman al backend GLSL existente — no son del registry porque dependen de estado interno de shaders_lab. 4.2 Mover el codigo correspondiente desde `main.cpp`. 4.3 `main.cpp` queda <250 lineas. ### Fase 5 — Indexar y validar 5.1 `./fn index` para registrar las 3 nuevas funciones puras. 5.2 Verificar `purity: pure`, `returns_optional: false`, `error_type: ""` en cada una. 5.3 Build Linux + Windows. Tests pasan. ## Decisiones - `sql_parse` es deliberadamente minimal — no es un parser SQL completo, solo distingue tipos de statement. Si en el futuro hace falta mas, se anade. - `compile_code/compile_dag` quedan como utilities de la app, NO en el registry (dependen de tipos internos de shaders_lab). ## Riesgos - Tests unitarios C++: no hay framework configurado todavia (issue 0047 lo monta). Mientras tanto, anadir tests dentro del archivo via `#ifdef TESTING` + un main pequeno, o esperar a 0047. - Decision: usar `#ifdef TESTING` por ahora; cuando 0047 monte Catch2, migrar. ## Validacion ```bash ./fn run sql_parse_cpp_core # ejecuta el test si lo hay ./fn show sql_parse_cpp_core ./fn show process_state_machine_cpp_core ./fn show file_poll_diff_cpp_core cmake --build cpp/build --target shaders_lab ```