# ZCATCONFIG - Sistema de Configuracion Declarativo > **IMPORTANTE PARA CLAUDE**: Lee la seccion "PROTOCOLO DE INICIO" antes de hacer cualquier cosa. --- ## PROTOCOLO DE INICIO (LEER PRIMERO) ### Paso 1: Leer normas del equipo ``` /mnt/cello2/arno/re/recode/teamdocs/LAST_UPDATE.md ``` ### Paso 2: Verificar estado del proyecto ```bash cd /mnt/cello2/arno/re/recode/zig/zcatconfig git status git log --oneline -5 PATH=/mnt/cello2/arno/re/recode/zig/zig-0.15.2/zig-x86_64-linux-0.15.2:$PATH zig build test ``` --- ## INFORMACION DEL PROYECTO | Campo | Valor | |-------|-------| | **Nombre** | zcatconfig | | **Version** | v0.2.3 | | **Fecha inicio** | 2025-12-17 | | **Estado** | FUNCIONAL - Integrado en zsimifactu (arquitectura 3 fuentes) | | **Lenguaje** | Zig 0.15.2 | | **Dependencias** | Ninguna (Zig puro) | ### Descripcion **zcatconfig** es una libreria para gestion de configuracion declarativa: - Definicion de variables con metadatos (tipo, default, descripcion, categoria) - Persistencia a archivo de texto legible con comentarios - Validacion de valores (rangos, enums, tipos) - Sistema Get/Set generico con inline for + @field - **ConfigManager**: Gestor autonomo con observers y auto-save - **FileWatcher**: Detecta cambios en archivo via mtime (hot-reload) - **loadFromString**: Carga config desde string embebido --- ## ORIGEN: zsimifactu config/ Esta libreria extrae y generaliza el sistema de configuracion implementado en zsimifactu. ### Archivos originales en zsimifactu: ``` src/config/ ├── config.zig # Re-exports publicos ├── types.zig # ConfigVariable, ConfigVarType, Color ├── variables.zig # Definiciones declarativas (proyecto-especifico) ├── structures.zig # Config struct (proyecto-especifico) ├── engine.zig # Meta-engine Get/Set con validacion └── persistence.zig # Load/Save archivo texto ``` ### Que se extrae a zcatconfig: - types.zig (completo) - engine.zig (generalizado) - persistence.zig (generalizado) ### Que queda en el proyecto consumidor: - variables.zig (definiciones especificas del proyecto) - structures.zig (struct Config especifico) --- ## ARQUITECTURA ``` ┌─────────────────────────────────────────────────────────────────┐ │ PROYECTO CONSUMIDOR │ │ │ │ variables.zig: │ │ pub const config_variables = [_]ConfigVariable{ │ │ .{ .name = "auto_save", .var_type = .boolean, ... }, │ │ .{ .name = "font_size", .var_type = .integer, ... }, │ │ }; │ │ │ │ structures.zig: │ │ pub const Config = struct { │ │ auto_save: bool = true, │ │ font_size: i32 = 14, │ │ }; │ ├─────────────────────────────────────────────────────────────────┤ │ ZCATCONFIG │ │ │ │ types.zig: │ │ ConfigVariable, ConfigVarType, ConfigResult, Color │ │ │ │ engine.zig: │ │ Engine(comptime variables, comptime ConfigStruct) │ │ - get(), set(), getByName(), setByName() │ │ - Validacion automatica │ │ │ │ persistence.zig: │ │ - load(config, path, variables) │ │ - save(config, path, variables) │ │ - Formato: @variable_name: valor # comentario │ └─────────────────────────────────────────────────────────────────┘ ``` --- ## API PROPUESTA ### Uso basico ```zig const zcatconfig = @import("zcatconfig"); // En el proyecto consumidor: const variables = @import("config/variables.zig"); const Config = @import("config/structures.zig").Config; // Crear engine tipado const Engine = zcatconfig.Engine(variables.config_variables, Config); // Uso var config = Config{}; Engine.load(&config, "app_config.txt") catch {}; config.font_size = 16; try Engine.save(&config, "app_config.txt"); ``` ### Tipos de variables soportados | Tipo | Zig Type | Formato archivo | |------|----------|-----------------| | boolean | bool | Si/No | | integer | i32 | 123 | | float | f32 | 1.5 | | string | []const u8 | texto libre | | color | Color | RGB(r,g,b) | | enum_tipo | enum | NombreVariante | --- ## COMANDOS ```bash # Compilar PATH=/mnt/cello2/arno/re/recode/zig/zig-0.15.2/zig-x86_64-linux-0.15.2:$PATH zig build # Tests PATH=/mnt/cello2/arno/re/recode/zig/zig-0.15.2/zig-x86_64-linux-0.15.2:$PATH zig build test ``` --- ## RUTAS ```bash # Este proyecto /mnt/cello2/arno/re/recode/zig/zcatconfig/ # Proyecto origen (referencia) /mnt/cello2/arno/re/recode/zig/zsimifactu/src/config/ # Documentacion equipo /mnt/cello2/arno/re/recode/teamdocs/ ``` --- ## PLAN DE TRABAJO ### Fase 1: Estructura base ✅ - [x] Crear proyecto (build.zig, CLAUDE.md) - [x] Extraer types.zig de zsimifactu - [x] Adaptar engine.zig (parametrizar variables y Config) - [x] Adaptar persistence.zig ### Fase 2: Generalizacion ✅ - [x] Engine generico con comptime - [x] Tests unitarios - [x] Documentacion API ### Fase 3: Integracion ✅ - [x] Integrar en zsimifactu como dependencia - [x] Verificar que zsimifactu funciona igual ### Fase 4: ConfigManager ✅ - [x] ConfigManager con loadOrCreate, auto-save, observers - [x] Sistema de observers con contexto - [x] isDirty tracking ### Fase 5: FileWatcher + Utils ✅ - [x] FileWatcher para hot-reload (mtime polling) - [x] loadFromString para defaults embebidos - [x] updateMtime para evitar auto-detectar cambios propios --- ## API PRINCIPAL ### ConfigManager (gestor autonomo) ```zig const zcatconfig = @import("zcatconfig"); // Crear ConfigManager tipado const MyConfigManager = zcatconfig.ConfigManager(&variables, MyConfig, "mi_app"); // Opcion 1: init() - Carga o crea archivo automaticamente var manager = try MyConfigManager.init(allocator, "config.txt"); defer manager.deinit(); // Auto-save si dirty // Opcion 2: initDeferred() - NO carga/crea archivo (control manual) var manager = MyConfigManager.initDeferred(allocator, "config.txt"); // Luego cargar manualmente cuando sea necesario: try manager.load(); // o manager.save() para crear // Acceso const cfg = manager.getConfig(); try manager.set("@mi_variable", "nuevo_valor"); // Observers manager.addObserver(myCallback, my_context); ``` ### initDeferred() - Control manual de archivo (v0.2.3) Cuando necesitas controlar **cuándo** se crea/carga el archivo (ej: arquitectura 3 fuentes): ```zig // Caso de uso: File > BD > Defaults // 1. Crear manager SIN cargar archivo var manager = MyConfigManager.initDeferred(allocator, "config.txt"); // 2. Verificar si existe archivo const archivo_existe = std.fs.cwd().access("config.txt", .{}) catch false; // 3. Verificar si BD tiene config const bd_tiene_config = checkDatabase(); // 4. Aplicar prioridad if (archivo_existe) { try manager.load(); // Cargar de archivo syncToBd(&manager); // Sync a BD } else if (bd_tiene_config) { loadFromBd(&manager); // Cargar de BD try manager.save(); // Crear archivo } else { try manager.save(); // Crear con defaults syncToBd(&manager); // Sync defaults a BD } ``` **Por qué initDeferred()?** - `init()` llama `loadOrCreate()` que crea el archivo con defaults - Si BD tiene valores, el archivo ya existe antes de poder verificar - `initDeferred()` permite verificar BD primero, luego decidir ### FileWatcher (hot-reload) ```zig var watcher = zcatconfig.FileWatcher.init("config.txt", 1000); // check cada 1s // En main loop: if (watcher.checkForChanges()) { // Recargar configuracion } // Despues de guardar: watcher.updateMtime(); // Evitar detectar nuestro propio cambio ``` ### loadFromString (defaults embebidos) ```zig const defaults = @embedFile("defaults.txt"); zcatconfig.loadFromString(&variables, Config, &config, defaults); ``` --- ## VALIDACION DE RANGOS (v0.2.3) ### Rangos negativos soportados La validación de rangos ahora soporta valores mínimos negativos: ```zig // Definición de variable con rango negativo .{ .name = "offset_x", .var_type = .integer, .range = "-100-100", ... } .{ .name = "offset_y", .var_type = .integer, .range = "-50-50", ... } .{ .name = "temperature", .var_type = .float, .range = "-40.0-85.0", ... } ``` ### Bug corregido (commit `957767d`) **Problema:** `splitScalar('-')` dividía `"-100-100"` como `["", "100", "100"]` **Solución:** Buscar el separador '-' excluyendo el primer carácter si es negativo: ```zig const separator_pos = blk: { const start: usize = if (range.len > 0 and range[0] == '-') 1 else 0; for (range[start..], start..) |c, i| { if (c == '-') break :blk i; } return false; }; ``` ### Formatos de rango válidos | Rango | Mínimo | Máximo | Notas | |-------|--------|--------|-------| | `"0-100"` | 0 | 100 | Positivo simple | | `"-100-100"` | -100 | 100 | Negativo a positivo | | `"-50-50"` | -50 | 50 | Centrado en cero | | `"-100-0"` | -100 | 0 | Solo negativos | --- ## EQUIPO - **Usuario (R.Eugenio)**: Desarrollador principal - **Claude**: Asistente de programacion (Claude Code / Opus 4.5) --- ## HISTORIAL | Fecha | Version | Commit | Cambios | |-------|---------|--------|---------| | 2025-12-18 | v0.2.3 | `957767d` | validateIntRange/Float fix: rangos negativos `"-100-100"` | | 2025-12-18 | v0.2.2 | `0ef5efd` | initDeferred(): control manual sin auto-crear archivo | | 2025-12-18 | v0.2.1 | - | FileWatcher, loadFromString, updateMtime | | 2025-12-17 | v0.2.0 | - | ConfigManager con observers, auto-save, isDirty | | 2025-12-17 | v0.1.0 | - | Proyecto creado, Engine + persistence |