docs: Documentar setSilent() y descripciones extendidas v0.2.4

- setSilent(): carga masiva sin disparar observers
- Formato extendido auto_validate: opcion=descripcion
- Genera comentarios multilínea en archivo config
- 100% compatible hacia atrás

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
R.Eugenio 2025-12-25 00:33:00 +01:00
parent 3dd835eb51
commit da50ebfc75
2 changed files with 150 additions and 17 deletions

100
CLAUDE.md
View file

@ -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 |

View file

@ -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);
}
}
}