diff --git a/CLAUDE.md b/CLAUDE.md index c714fcc..d785417 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -26,7 +26,7 @@ PATH=/mnt/cello2/arno/re/recode/zig/zig-0.15.2/zig-x86_64-linux-0.15.2:$PATH zig | Campo | Valor | |-------|-------| | **Nombre** | zcatconfig | -| **Version** | v0.2.3 | +| **Version** | v0.2.4 | | **Fecha inicio** | 2025-12-17 | | **Estado** | FUNCIONAL - Integrado en zsimifactu (arquitectura 3 fuentes) | | **Lenguaje** | Zig 0.15.2 | @@ -321,6 +321,103 @@ const separator_pos = blk: { --- +## setSilent() - Carga masiva sin observers (v0.2.4) + +### Problema + +Al cargar config desde BD, cada `set()` dispara observers que escriben de vuelta a BD → loop infinito de logs. + +### Solución + +`setSilent()` establece valores SIN notificar observers: + +```zig +// Cargar desde BD sin disparar observers +var stmt = try db.prepare("SELECT config_key, config_value FROM config_backup"); +while (try stmt.step()) { + const key = stmt.columnText(0) orelse continue; + const value = stmt.columnText(1) orelse continue; + // Usar setSilent en vez de set + manager.setSilent(key, value) catch continue; +} +``` + +### API + +```zig +/// Establece valor SIN notificar observers +/// Útil para carga masiva desde BD +pub fn setSilent(self: *Self, key: []const u8, value: []const u8) ConfigError!void +``` + +--- + +## DESCRIPCIONES EXTENDIDAS EN ARCHIVO CONFIG (v0.2.4) + +### Problema + +El archivo de configuración mostraba opciones pero no explicaba qué hacía cada una: + +``` +@validacion_nif_modo: Estricto # Modo validación NIF [Estricto,Permisivo,Desactivado] +``` + +El usuario ve las opciones pero no sabe qué significa cada una. + +### Solución: Formato extendido en auto_validate + +En `config_variables`, usar formato `opcion=descripcion`: + +```zig +// Formato simple (sin descripciones): +.{ "mi_var", .string, .cat, "Op1,Op2,Op3", "Descripción", ... } + +// Formato extendido (con descripciones): +.{ "mi_var", .string, .cat, "Op1=Hace X,Op2=Hace Y,Op3=Hace Z", "Descripción", ... } +``` + +### Resultado en archivo config + +**Formato simple** (sin `=`): +``` +@mi_var: Op1 # Descripción [Op1,Op2,Op3] +``` + +**Formato extendido** (con `=`): +``` +# Descripción +# Op1: Hace X +# Op2: Hace Y +# Op3: Hace Z +@mi_var: Op1 +``` + +### Ejemplo real + +```zig +// En config_unified.zig: +.{ "validacion_nif_modo", .string, Cat.validaciones, + "Estricto=Bloquea guardar si NIF inválido,Permisivo=Avisa pero permite guardar,Desactivado=Sin validación", + "Modo validación NIF", false, "# VALIDACIONES" }, +``` + +Genera: +``` +# Modo validación NIF +# Estricto: Bloquea guardar si NIF inválido +# Permisivo: Avisa pero permite guardar +# Desactivado: Sin validación +@validacion_nif_modo: Estricto +``` + +### Compatibilidad + +- Si `auto_validate` NO contiene `=` → formato simple (actual) +- Si `auto_validate` contiene `=` → formato extendido (nuevo) +- 100% compatible con archivos existentes + +--- + ## EQUIPO - **Usuario (R.Eugenio)**: Desarrollador principal @@ -332,6 +429,7 @@ const separator_pos = blk: { | Fecha | Version | Commit | Cambios | |-------|---------|--------|---------| +| 2025-12-25 | v0.2.4 | `3dd835e` | setSilent() + descripciones extendidas en archivo config | | 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 | diff --git a/src/zcatconfig.zig b/src/zcatconfig.zig index 4abfe99..6d2a746 100644 --- a/src/zcatconfig.zig +++ b/src/zcatconfig.zig @@ -563,28 +563,63 @@ pub fn save( const value_len = value.len; const total_len = key_len + 2 + value_len; - // Construir comentario con opciones válidas si existen - var comment_buf: [256]u8 = undefined; - const comment: []const u8 = if (v.auto_validate) |valid| - std.fmt.bufPrint(&comment_buf, "{s} [{s}]", .{ v.description, valid }) catch v.description + // Verificar si tiene descripciones extendidas (formato "opcion=desc,opcion2=desc2") + const has_descriptions = if (v.auto_validate) |valid| + std.mem.indexOf(u8, valid, "=") != null else - v.description; + false; - if (total_len < 40 and comment.len > 0) { - const padding = 40 - total_len; - const line = std.fmt.bufPrint(&line_buf, "{s}: {s}{s}# {s}\n", .{ - v.config_key, - value, - spaces(padding), - comment, - }) catch unreachable; // Buffer 1024 es suficiente - try file.writeAll(line); - } else { + if (has_descriptions) { + // Formato extendido: comentarios multilínea + // # Descripción de la variable + // # Opcion1: Descripción 1 + // # Opcion2: Descripción 2 + // @variable: valor + var desc_line: [256]u8 = undefined; + const desc_text = std.fmt.bufPrint(&desc_line, "# {s}\n", .{v.description}) catch ""; + try file.writeAll(desc_text); + + // Parsear opciones con descripciones + var options_iter = std.mem.splitScalar(u8, v.auto_validate.?, ','); + while (options_iter.next()) |option_desc| { + if (std.mem.indexOf(u8, option_desc, "=")) |eq_pos| { + const opt_name = option_desc[0..eq_pos]; + const opt_desc = option_desc[eq_pos + 1 ..]; + const opt_line = std.fmt.bufPrint(&desc_line, "# {s}: {s}\n", .{ opt_name, opt_desc }) catch ""; + try file.writeAll(opt_line); + } + } + + // Escribir variable sin comentario inline const line = std.fmt.bufPrint(&line_buf, "{s}: {s}\n", .{ v.config_key, value, - }) catch unreachable; // Buffer 1024 es suficiente + }) catch unreachable; try file.writeAll(line); + } else { + // Formato simple: comentario inline con opciones entre corchetes + var comment_buf: [256]u8 = undefined; + const comment: []const u8 = if (v.auto_validate) |valid| + std.fmt.bufPrint(&comment_buf, "{s} [{s}]", .{ v.description, valid }) catch v.description + else + v.description; + + if (total_len < 40 and comment.len > 0) { + const padding = 40 - total_len; + const line = std.fmt.bufPrint(&line_buf, "{s}: {s}{s}# {s}\n", .{ + v.config_key, + value, + spaces(padding), + comment, + }) catch unreachable; + try file.writeAll(line); + } else { + const line = std.fmt.bufPrint(&line_buf, "{s}: {s}\n", .{ + v.config_key, + value, + }) catch unreachable; + try file.writeAll(line); + } } }