feat: ConfigManager - gestor autónomo de configuración
Añade ConfigManager que maneja automáticamente: - Carga/creación de archivo config (loadOrCreate) - Auto-guardado en deinit si hay cambios pendientes - Sistema de observers con contexto para sincronización externa Cambios: - ConfigManager(variables, ConfigType, app_name) type - Observer con contexto: fn(change, config, ctx) void - addObserver(callback, context) para registrar listeners - Métodos: get, set, getConfig, getConfigMut, markDirty, isDirty 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
15c7f7357e
commit
91e5133e13
3 changed files with 1094 additions and 176 deletions
18
CLAUDE.md
18
CLAUDE.md
|
|
@ -169,18 +169,18 @@ PATH=/mnt/cello2/arno/re/recode/zig/zig-0.15.2/zig-x86_64-linux-0.15.2:$PATH zig
|
||||||
|
|
||||||
## PLAN DE TRABAJO
|
## PLAN DE TRABAJO
|
||||||
|
|
||||||
### Fase 1: Estructura base
|
### Fase 1: Estructura base - COMPLETADA
|
||||||
- [x] Crear proyecto (build.zig, CLAUDE.md)
|
- [x] Crear proyecto (build.zig, CLAUDE.md)
|
||||||
- [ ] Extraer types.zig de zsimifactu
|
- [x] Extraer types.zig de zsimifactu
|
||||||
- [ ] Adaptar engine.zig (parametrizar variables y Config)
|
- [x] Adaptar engine.zig (parametrizar variables y Config)
|
||||||
- [ ] Adaptar persistence.zig
|
- [x] Adaptar persistence.zig
|
||||||
|
|
||||||
### Fase 2: Generalizacion
|
### Fase 2: Generalizacion - COMPLETADA
|
||||||
- [ ] Engine generico con comptime
|
- [x] Engine generico con comptime
|
||||||
- [ ] Tests unitarios
|
- [x] Tests unitarios (9 tests)
|
||||||
- [ ] Documentacion API
|
- [x] Documentacion API
|
||||||
|
|
||||||
### Fase 3: Integracion
|
### Fase 3: Integracion - EN PROGRESO
|
||||||
- [ ] Integrar en zsimifactu como dependencia
|
- [ ] Integrar en zsimifactu como dependencia
|
||||||
- [ ] Verificar que zsimifactu funciona igual
|
- [ ] Verificar que zsimifactu funciona igual
|
||||||
|
|
||||||
|
|
|
||||||
348
docs/CONFIGMANAGER_DESIGN.md
Normal file
348
docs/CONFIGMANAGER_DESIGN.md
Normal file
|
|
@ -0,0 +1,348 @@
|
||||||
|
# ConfigManager - Diseño e Implementación
|
||||||
|
|
||||||
|
**Fecha:** 2025-12-17
|
||||||
|
**Versión:** v0.2.0
|
||||||
|
**Estado:** En implementación
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Objetivo
|
||||||
|
|
||||||
|
Crear un gestor de configuración completo y autónomo que:
|
||||||
|
1. Gestione automáticamente el archivo de configuración
|
||||||
|
2. Mantenga las variables en memoria
|
||||||
|
3. Notifique cambios via observers (para sincronizar con BD u otros sistemas)
|
||||||
|
4. Minimice el trabajo requerido en proyectos futuros
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Filosofía
|
||||||
|
|
||||||
|
> "Invertir trabajo ahora para ahorrar problemas y trabajo en el futuro"
|
||||||
|
|
||||||
|
La librería debe hacer todo el trabajo pesado. El programa solo debe:
|
||||||
|
1. Definir las variables (array de ConfigVariable)
|
||||||
|
2. Definir el struct Config
|
||||||
|
3. Crear el ConfigManager
|
||||||
|
4. Opcionalmente registrar observers
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Arquitectura
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ ZCATCONFIG │
|
||||||
|
│ (Librería independiente, reutilizable) │
|
||||||
|
├─────────────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ ConfigManager(comptime variables, comptime ConfigType) │
|
||||||
|
│ ├── allocator: Allocator │
|
||||||
|
│ ├── config: ConfigType ← Datos en memoria │
|
||||||
|
│ ├── file_path: []const u8 ← Ruta archivo │
|
||||||
|
│ ├── app_name: []const u8 ← Nombre app (para headers) │
|
||||||
|
│ ├── dirty: bool ← Cambios pendientes │
|
||||||
|
│ ├── observers: []Observer ← Lista de callbacks │
|
||||||
|
│ │ │
|
||||||
|
│ ├── init(allocator, path, app_name) │
|
||||||
|
│ │ └── Llama loadOrCreate() automáticamente │
|
||||||
|
│ │ │
|
||||||
|
│ ├── deinit() │
|
||||||
|
│ │ └── Auto-save si dirty, libera recursos │
|
||||||
|
│ │ │
|
||||||
|
│ ├── get(key) → ConfigResult │
|
||||||
|
│ ├── set(key, value) → !void │
|
||||||
|
│ │ └── Marca dirty=true, notifica observers │
|
||||||
|
│ │ │
|
||||||
|
│ ├── getConfig() → *ConfigType │
|
||||||
|
│ │ └── Acceso directo al struct (para campos tipados) │
|
||||||
|
│ │ │
|
||||||
|
│ ├── save() → !void │
|
||||||
|
│ ├── load() → !void │
|
||||||
|
│ ├── loadOrCreate() → !void │
|
||||||
|
│ │ └── Si archivo no existe, guarda defaults │
|
||||||
|
│ │ │
|
||||||
|
│ ├── addObserver(callback) → void │
|
||||||
|
│ ├── removeObserver(callback) → void │
|
||||||
|
│ └── notifyObservers(change) → void (interno) │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tipos
|
||||||
|
|
||||||
|
### ConfigChange
|
||||||
|
|
||||||
|
Información sobre un cambio de configuración:
|
||||||
|
|
||||||
|
```zig
|
||||||
|
pub const ConfigChange = struct {
|
||||||
|
key: []const u8, // Clave que cambió ("@auto_guardar")
|
||||||
|
variable: *const ConfigVariable, // Definición de la variable
|
||||||
|
old_value: ConfigResult, // Valor anterior
|
||||||
|
new_value: ConfigResult, // Valor nuevo
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Observer
|
||||||
|
|
||||||
|
Callback que recibe notificaciones de cambios:
|
||||||
|
|
||||||
|
```zig
|
||||||
|
pub const Observer = *const fn (change: ConfigChange, config: *const ConfigType) void;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Flujo de Uso
|
||||||
|
|
||||||
|
### Uso Básico (sin BD)
|
||||||
|
|
||||||
|
```zig
|
||||||
|
const zcatconfig = @import("zcatconfig");
|
||||||
|
|
||||||
|
// Definir variables (en variables.zig)
|
||||||
|
const config_variables = [_]zcatconfig.ConfigVariable{
|
||||||
|
.{ .name = "timeout", .config_key = "@timeout", ... },
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
|
||||||
|
// Definir struct (en structures.zig)
|
||||||
|
const Config = struct {
|
||||||
|
timeout: u32 = 30,
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
|
||||||
|
// En main o donde sea necesario:
|
||||||
|
pub fn main() !void {
|
||||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
const allocator = gpa.allocator();
|
||||||
|
|
||||||
|
// Crear manager - carga o crea archivo automáticamente
|
||||||
|
var config_manager = try zcatconfig.ConfigManager(
|
||||||
|
&config_variables,
|
||||||
|
Config
|
||||||
|
).init(allocator, "mi_app_config.txt", "MiApp");
|
||||||
|
defer config_manager.deinit(); // Auto-save si hay cambios
|
||||||
|
|
||||||
|
// Usar configuración
|
||||||
|
const timeout = config_manager.getConfig().timeout;
|
||||||
|
|
||||||
|
// Cambiar valor (notifica observers, marca dirty)
|
||||||
|
try config_manager.set("@timeout", "60");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Uso con Sincronización a BD
|
||||||
|
|
||||||
|
```zig
|
||||||
|
// En DataManager:
|
||||||
|
pub const DataManager = struct {
|
||||||
|
config_manager: *zcatconfig.ConfigManager(&config_variables, Config),
|
||||||
|
db: *Database,
|
||||||
|
|
||||||
|
pub fn init(allocator: Allocator, db: *Database) !*Self {
|
||||||
|
var self = try allocator.create(Self);
|
||||||
|
|
||||||
|
// Crear config manager
|
||||||
|
self.config_manager = try zcatconfig.ConfigManager(
|
||||||
|
&config_variables,
|
||||||
|
Config
|
||||||
|
).init(allocator, "config.txt", "zsimifactu");
|
||||||
|
|
||||||
|
// Registrar observer para sincronizar con BD
|
||||||
|
self.config_manager.addObserver(syncToDatabase);
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn syncToDatabase(change: ConfigChange, config: *const Config) void {
|
||||||
|
// INSERT OR REPLACE INTO config (key, value) VALUES (?, ?)
|
||||||
|
// Usar change.key y change.new_value
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Comportamiento Automático
|
||||||
|
|
||||||
|
### En init()
|
||||||
|
|
||||||
|
1. Inicializa Config con valores por defecto
|
||||||
|
2. Llama `loadOrCreate()`:
|
||||||
|
- Si archivo existe → carga valores
|
||||||
|
- Si no existe → guarda archivo con defaults
|
||||||
|
3. `dirty = false`
|
||||||
|
|
||||||
|
### En set()
|
||||||
|
|
||||||
|
1. Obtiene valor actual (para ConfigChange)
|
||||||
|
2. Aplica nuevo valor al struct
|
||||||
|
3. `dirty = true`
|
||||||
|
4. Notifica a todos los observers
|
||||||
|
|
||||||
|
### En deinit()
|
||||||
|
|
||||||
|
1. Si `dirty == true` → llama `save()`
|
||||||
|
2. Libera recursos del Config (strings alocados)
|
||||||
|
3. Libera lista de observers
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Integración con zsimifactu
|
||||||
|
|
||||||
|
### Antes (actual)
|
||||||
|
|
||||||
|
```
|
||||||
|
DataManager
|
||||||
|
├── config: Config ← Struct directo
|
||||||
|
├── config_file_path: []const u8
|
||||||
|
├── config_dirty: bool
|
||||||
|
├── loadConfig() / saveConfig() ← Métodos manuales
|
||||||
|
└── getConfigValue() / setConfigValue()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Después (con ConfigManager)
|
||||||
|
|
||||||
|
```
|
||||||
|
DataManager
|
||||||
|
├── config_manager: *ConfigManager ← Gestor completo
|
||||||
|
└── (todo lo demás delegado al manager)
|
||||||
|
|
||||||
|
// Acceso a config:
|
||||||
|
dm.config_manager.getConfig().mi_campo
|
||||||
|
|
||||||
|
// Cambiar valor:
|
||||||
|
try dm.config_manager.set("@mi_campo", "valor");
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sincronización con BD
|
||||||
|
|
||||||
|
### Estrategia
|
||||||
|
|
||||||
|
1. ConfigManager notifica cambios via observer
|
||||||
|
2. DataManager registra un observer que escribe a BD
|
||||||
|
3. Al iniciar, se puede cargar desde BD y aplicar al ConfigManager
|
||||||
|
|
||||||
|
### Tabla BD (ya existe en zsimifactu)
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE IF NOT EXISTS config (
|
||||||
|
key TEXT PRIMARY KEY,
|
||||||
|
value TEXT NOT NULL,
|
||||||
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Flujo de Sincronización
|
||||||
|
|
||||||
|
```
|
||||||
|
Inicio:
|
||||||
|
1. ConfigManager.init() → carga archivo (o crea con defaults)
|
||||||
|
2. DataManager carga valores de BD
|
||||||
|
3. Aplica valores BD al ConfigManager (si BD tiene prioridad)
|
||||||
|
|
||||||
|
Durante ejecución:
|
||||||
|
1. Usuario cambia valor
|
||||||
|
2. ConfigManager.set() → actualiza memoria + notifica
|
||||||
|
3. Observer → escribe a BD
|
||||||
|
4. ConfigManager marca dirty
|
||||||
|
|
||||||
|
Al cerrar:
|
||||||
|
1. ConfigManager.deinit() → guarda archivo (si dirty)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prioridad de Valores
|
||||||
|
|
||||||
|
Orden de prioridad (mayor a menor):
|
||||||
|
1. **Cambios en runtime** - Siempre prevalecen
|
||||||
|
2. **Base de datos** - Persistencia principal
|
||||||
|
3. **Archivo config** - Backup/portabilidad
|
||||||
|
4. **Defaults en código** - Fallback
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Archivos Afectados
|
||||||
|
|
||||||
|
### zcatconfig (crear/modificar)
|
||||||
|
|
||||||
|
- `src/zcatconfig.zig` - Añadir ConfigManager
|
||||||
|
- `docs/CONFIGMANAGER_DESIGN.md` - Este documento
|
||||||
|
|
||||||
|
### zsimifactu (modificar)
|
||||||
|
|
||||||
|
- `src/data_manager/data_manager.zig` - Usar ConfigManager
|
||||||
|
- `src/config/config.zig` - Simplificar (delegar a zcatconfig)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Plan de Implementación
|
||||||
|
|
||||||
|
1. **Implementar ConfigManager en zcatconfig**
|
||||||
|
- Struct con todos los campos
|
||||||
|
- init/deinit con loadOrCreate automático
|
||||||
|
- get/set con notificación
|
||||||
|
- Sistema de observers
|
||||||
|
|
||||||
|
2. **Actualizar zsimifactu**
|
||||||
|
- DataManager usa ConfigManager
|
||||||
|
- Registrar observer para BD
|
||||||
|
- Eliminar código duplicado
|
||||||
|
|
||||||
|
3. **Probar**
|
||||||
|
- Borrar archivo config → debe regenerarse
|
||||||
|
- Cambiar valor → debe notificar y guardar
|
||||||
|
- Cerrar app → debe auto-guardar
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Notas de Implementación Zig
|
||||||
|
|
||||||
|
### Observers como ArrayList
|
||||||
|
|
||||||
|
```zig
|
||||||
|
observers: std.ArrayList(Observer),
|
||||||
|
|
||||||
|
pub fn addObserver(self: *Self, observer: Observer) void {
|
||||||
|
self.observers.append(self.allocator, observer) catch {};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notifyObservers(self: *Self, change: ConfigChange) void {
|
||||||
|
for (self.observers.items) |observer| {
|
||||||
|
observer(change, &self.config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### ConfigManager como tipo genérico
|
||||||
|
|
||||||
|
```zig
|
||||||
|
pub fn ConfigManager(
|
||||||
|
comptime variables: []const ConfigVariable,
|
||||||
|
comptime ConfigType: type,
|
||||||
|
) type {
|
||||||
|
return struct {
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
config: ConfigType,
|
||||||
|
file_path: []const u8,
|
||||||
|
app_name: []const u8,
|
||||||
|
dirty: bool,
|
||||||
|
observers: std.ArrayList(Observer),
|
||||||
|
|
||||||
|
pub const Observer = *const fn (ConfigChange, *const ConfigType) void;
|
||||||
|
|
||||||
|
pub fn init(...) !Self { ... }
|
||||||
|
pub fn deinit(self: *Self) void { ... }
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue