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
|
||||
|
||||
### Fase 1: Estructura base
|
||||
### Fase 1: Estructura base - COMPLETADA
|
||||
- [x] Crear proyecto (build.zig, CLAUDE.md)
|
||||
- [ ] Extraer types.zig de zsimifactu
|
||||
- [ ] Adaptar engine.zig (parametrizar variables y Config)
|
||||
- [ ] Adaptar persistence.zig
|
||||
- [x] Extraer types.zig de zsimifactu
|
||||
- [x] Adaptar engine.zig (parametrizar variables y Config)
|
||||
- [x] Adaptar persistence.zig
|
||||
|
||||
### Fase 2: Generalizacion
|
||||
- [ ] Engine generico con comptime
|
||||
- [ ] Tests unitarios
|
||||
- [ ] Documentacion API
|
||||
### Fase 2: Generalizacion - COMPLETADA
|
||||
- [x] Engine generico con comptime
|
||||
- [x] Tests unitarios (9 tests)
|
||||
- [x] Documentacion API
|
||||
|
||||
### Fase 3: Integracion
|
||||
### Fase 3: Integracion - EN PROGRESO
|
||||
- [ ] Integrar en zsimifactu como dependencia
|
||||
- [ ] 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