build: Migrar a Zig 0.16
- Propagar io: std.Io en metodos de ConfigManager, load y save. - Actualizar FileWatcher para usar std.Io.Dir.statFile y manejar nuevo Io.Timestamp (i96). - Implementar helpers currentTimestamp y currentMilliTimestamp usando std.posix.clock_gettime. - Actualizar build.zig.zon a 0.16.0. Co-Authored-By: Gemini <noreply@google.com>
This commit is contained in:
parent
85d50fd920
commit
c761cb838d
2 changed files with 62 additions and 47 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
.{
|
.{
|
||||||
.name = .zcatconfig,
|
.name = .zcatconfig,
|
||||||
.version = "0.1.0",
|
.version = "0.1.0",
|
||||||
.minimum_zig_version = "0.15.0",
|
.minimum_zig_version = "0.16.0",
|
||||||
.fingerprint = 0x6211a84c80e77eb,
|
.fingerprint = 0x6211a84c80e77eb,
|
||||||
|
|
||||||
.dependencies = .{},
|
.dependencies = .{},
|
||||||
|
|
|
||||||
|
|
@ -460,9 +460,15 @@ fn parseBool(value: []const u8) ?bool {
|
||||||
// PERSISTENCE
|
// PERSISTENCE
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
|
fn currentMilliTimestamp() i64 {
|
||||||
|
const ts = std.posix.clock_gettime(std.posix.CLOCK.REALTIME) catch return 0;
|
||||||
|
return ts.sec * 1000 + @divTrunc(ts.nsec, 1_000_000);
|
||||||
|
}
|
||||||
|
|
||||||
/// Carga configuracion desde archivo.
|
/// Carga configuracion desde archivo.
|
||||||
/// Requiere que ConfigType tenga un campo `allocator`.
|
/// Requiere que ConfigType tenga un campo `allocator`.
|
||||||
pub fn load(
|
pub fn load(
|
||||||
|
io: std.Io,
|
||||||
comptime variables: []const ConfigVariable,
|
comptime variables: []const ConfigVariable,
|
||||||
comptime ConfigType: type,
|
comptime ConfigType: type,
|
||||||
config: *ConfigType,
|
config: *ConfigType,
|
||||||
|
|
@ -470,9 +476,13 @@ pub fn load(
|
||||||
) !void {
|
) !void {
|
||||||
const EngineType = Engine(variables, ConfigType);
|
const EngineType = Engine(variables, ConfigType);
|
||||||
|
|
||||||
// Leer archivo completo (propaga FileNotFound para que loadOrCreate pueda crear)
|
// Leer archivo completo
|
||||||
const content = try std.fs.cwd().readFileAlloc(config.allocator, path, 1024 * 1024);
|
const file = try std.Io.Dir.cwd().openFile(io, path, .{});
|
||||||
|
defer file.close(io);
|
||||||
|
const stat = try file.stat(io);
|
||||||
|
const content = try config.allocator.alloc(u8, @intCast(stat.size));
|
||||||
defer config.allocator.free(content);
|
defer config.allocator.free(content);
|
||||||
|
_ = try file.readPositional(io, &.{content}, 0);
|
||||||
|
|
||||||
// Parsear linea por linea
|
// Parsear linea por linea
|
||||||
var lines = std.mem.splitScalar(u8, content, '\n');
|
var lines = std.mem.splitScalar(u8, content, '\n');
|
||||||
|
|
@ -522,6 +532,7 @@ fn findCommentStart(text: []const u8) ?usize {
|
||||||
|
|
||||||
/// Guarda configuracion a archivo
|
/// Guarda configuracion a archivo
|
||||||
pub fn save(
|
pub fn save(
|
||||||
|
io: std.Io,
|
||||||
comptime variables: []const ConfigVariable,
|
comptime variables: []const ConfigVariable,
|
||||||
comptime ConfigType: type,
|
comptime ConfigType: type,
|
||||||
config: *const ConfigType,
|
config: *const ConfigType,
|
||||||
|
|
@ -530,20 +541,17 @@ pub fn save(
|
||||||
) !void {
|
) !void {
|
||||||
const EngineType = Engine(variables, ConfigType);
|
const EngineType = Engine(variables, ConfigType);
|
||||||
|
|
||||||
const file = try std.fs.cwd().createFile(path, .{});
|
const file = try std.Io.Dir.cwd().createFile(io, path, .{});
|
||||||
defer file.close();
|
defer file.close(io);
|
||||||
|
|
||||||
// Header del archivo
|
// Header del archivo
|
||||||
try file.writeAll("# ============================================================================\n");
|
try file.writeStreamingAll(io, "# ============================================================================\n");
|
||||||
var header_buf: [128]u8 = undefined;
|
var header_buf: [128]u8 = undefined;
|
||||||
const header_line = std.fmt.bufPrint(&header_buf, "# {s} - Archivo de Configuracion\n", .{app_name}) catch "";
|
const header_line = std.fmt.bufPrint(&header_buf, "# {s} - Archivo de Configuracion\n", .{app_name}) catch "";
|
||||||
try file.writeAll(header_line);
|
try file.writeStreamingAll(io, header_line);
|
||||||
try file.writeAll("# ============================================================================\n");
|
try file.writeStreamingAll(io, "# ============================================================================\n");
|
||||||
try file.writeAll("# \n");
|
try file.writeStreamingAll(io, "# \n");
|
||||||
try file.writeAll("# Este archivo se genera automaticamente. Puedes editarlo manualmente.\n");
|
try file.writeAll(io, &.{ "# Este archivo se genera automaticamente. Puedes editarlo manualmente.\n", "# Formato: @variable: valor # descripcion\n", "# \n", "\n" });
|
||||||
try file.writeAll("# Formato: @variable: valor # descripcion\n");
|
|
||||||
try file.writeAll("# \n");
|
|
||||||
try file.writeAll("\n");
|
|
||||||
|
|
||||||
var current_category: ?usize = null;
|
var current_category: ?usize = null;
|
||||||
var value_buf: [256]u8 = undefined;
|
var value_buf: [256]u8 = undefined;
|
||||||
|
|
@ -556,13 +564,13 @@ pub fn save(
|
||||||
// Header de categoria si cambio
|
// Header de categoria si cambio
|
||||||
if (current_category == null or current_category.? != v.category) {
|
if (current_category == null or current_category.? != v.category) {
|
||||||
if (current_category != null) {
|
if (current_category != null) {
|
||||||
try file.writeAll("\n");
|
try file.writeStreamingAll(io, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Si la variable tiene header personalizado, usarlo
|
// Si la variable tiene header personalizado, usarlo
|
||||||
if (v.category_header) |header| {
|
if (v.category_header) |header| {
|
||||||
try file.writeAll(header);
|
try file.writeStreamingAll(io, header);
|
||||||
try file.writeAll("\n");
|
try file.writeStreamingAll(io, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
current_category = v.category;
|
current_category = v.category;
|
||||||
|
|
@ -591,7 +599,7 @@ pub fn save(
|
||||||
// @variable: valor
|
// @variable: valor
|
||||||
var desc_line: [256]u8 = undefined;
|
var desc_line: [256]u8 = undefined;
|
||||||
const desc_text = std.fmt.bufPrint(&desc_line, "# {s}\n", .{v.description}) catch "";
|
const desc_text = std.fmt.bufPrint(&desc_line, "# {s}\n", .{v.description}) catch "";
|
||||||
try file.writeAll(desc_text);
|
try file.writeStreamingAll(io, desc_text);
|
||||||
|
|
||||||
// Parsear opciones con descripciones
|
// Parsear opciones con descripciones
|
||||||
var options_iter = std.mem.splitScalar(u8, v.auto_validate.?, ',');
|
var options_iter = std.mem.splitScalar(u8, v.auto_validate.?, ',');
|
||||||
|
|
@ -600,7 +608,7 @@ pub fn save(
|
||||||
const opt_name = option_desc[0..eq_pos];
|
const opt_name = option_desc[0..eq_pos];
|
||||||
const opt_desc = option_desc[eq_pos + 1 ..];
|
const opt_desc = option_desc[eq_pos + 1 ..];
|
||||||
const opt_line = std.fmt.bufPrint(&desc_line, "# {s}: {s}\n", .{ opt_name, opt_desc }) catch "";
|
const opt_line = std.fmt.bufPrint(&desc_line, "# {s}: {s}\n", .{ opt_name, opt_desc }) catch "";
|
||||||
try file.writeAll(opt_line);
|
try file.writeStreamingAll(io, opt_line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -609,7 +617,7 @@ pub fn save(
|
||||||
v.config_key,
|
v.config_key,
|
||||||
value,
|
value,
|
||||||
}) catch unreachable;
|
}) catch unreachable;
|
||||||
try file.writeAll(line);
|
try file.writeStreamingAll(io, line);
|
||||||
} else {
|
} else {
|
||||||
// Formato simple: comentario inline con opciones entre corchetes
|
// Formato simple: comentario inline con opciones entre corchetes
|
||||||
var comment_buf: [256]u8 = undefined;
|
var comment_buf: [256]u8 = undefined;
|
||||||
|
|
@ -626,21 +634,21 @@ pub fn save(
|
||||||
spaces(padding),
|
spaces(padding),
|
||||||
comment,
|
comment,
|
||||||
}) catch unreachable;
|
}) catch unreachable;
|
||||||
try file.writeAll(line);
|
try file.writeStreamingAll(io, line);
|
||||||
} else {
|
} else {
|
||||||
const line = std.fmt.bufPrint(&line_buf, "{s}: {s}\n", .{
|
const line = std.fmt.bufPrint(&line_buf, "{s}: {s}\n", .{
|
||||||
v.config_key,
|
v.config_key,
|
||||||
value,
|
value,
|
||||||
}) catch unreachable;
|
}) catch unreachable;
|
||||||
try file.writeAll(line);
|
try file.writeStreamingAll(io, line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try file.writeAll("\n");
|
try file.writeStreamingAll(io, "\n");
|
||||||
try file.writeAll("# ============================================================================\n");
|
try file.writeStreamingAll(io, "# ============================================================================\n");
|
||||||
try file.writeAll("# Fin del archivo de configuracion\n");
|
try file.writeStreamingAll(io, "# Fin del archivo de configuracion\n");
|
||||||
try file.writeAll("# ============================================================================\n");
|
try file.writeStreamingAll(io, "# ============================================================================\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Genera string de N espacios (maximo 64)
|
/// Genera string de N espacios (maximo 64)
|
||||||
|
|
@ -649,13 +657,19 @@ fn spaces(n: usize) []const u8 {
|
||||||
return space_buf[0..@min(n, 64)];
|
return space_buf[0..@min(n, 64)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn currentTimestamp() i64 {
|
||||||
|
const ts = std.posix.clock_gettime(std.posix.CLOCK.REALTIME) catch return 0;
|
||||||
|
return ts.sec;
|
||||||
|
}
|
||||||
|
|
||||||
/// Crea backup del archivo de configuracion
|
/// Crea backup del archivo de configuracion
|
||||||
pub fn createBackup(path: []const u8) !void {
|
pub fn createBackup(io: std.Io, path: []const u8) !void {
|
||||||
const now = std.time.timestamp();
|
const now = currentTimestamp();
|
||||||
var backup_path_buf: [512]u8 = undefined;
|
var backup_path_buf: [512]u8 = undefined;
|
||||||
const backup_path = std.fmt.bufPrint(&backup_path_buf, "{s}.{d}.bak", .{ path, now }) catch return;
|
const backup_path = std.fmt.bufPrint(&backup_path_buf, "{s}.{d}.bak", .{ path, now }) catch return;
|
||||||
|
|
||||||
std.fs.cwd().copyFile(path, std.fs.cwd(), backup_path, .{}) catch |err| {
|
const my_cwd = std.Io.Dir.cwd();
|
||||||
|
my_cwd.copyFile(my_cwd, path, my_cwd, backup_path, io, .{}) catch |err| {
|
||||||
if (err == error.FileNotFound) return;
|
if (err == error.FileNotFound) return;
|
||||||
return err;
|
return err;
|
||||||
};
|
};
|
||||||
|
|
@ -720,6 +734,7 @@ pub fn ConfigManager(
|
||||||
/// - Crea Config con valores por defecto
|
/// - Crea Config con valores por defecto
|
||||||
/// - Llama loadOrCreate() automaticamente
|
/// - Llama loadOrCreate() automaticamente
|
||||||
pub fn init(
|
pub fn init(
|
||||||
|
io: std.Io,
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
file_path: []const u8,
|
file_path: []const u8,
|
||||||
) !Self {
|
) !Self {
|
||||||
|
|
@ -732,7 +747,7 @@ pub fn ConfigManager(
|
||||||
};
|
};
|
||||||
|
|
||||||
// Cargar o crear archivo automaticamente
|
// Cargar o crear archivo automaticamente
|
||||||
try self.loadOrCreate();
|
try self.loadOrCreate(io);
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
@ -756,10 +771,10 @@ pub fn ConfigManager(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Libera recursos y guarda si hay cambios pendientes
|
/// Libera recursos y guarda si hay cambios pendientes
|
||||||
pub fn deinit(self: *Self) void {
|
pub fn deinit(self: *Self, io: std.Io) void {
|
||||||
// Auto-save si hay cambios
|
// Auto-save si hay cambios
|
||||||
if (self.dirty) {
|
if (self.dirty) {
|
||||||
self.save() catch {};
|
self.save(io) catch {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Liberar config
|
// Liberar config
|
||||||
|
|
@ -834,23 +849,23 @@ pub fn ConfigManager(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Carga configuracion desde archivo
|
/// Carga configuracion desde archivo
|
||||||
pub fn load(self: *Self) !void {
|
pub fn load(self: *Self, io: std.Io) !void {
|
||||||
try loadFn(variables, ConfigType, &self.config, self.file_path);
|
try loadFn(io, variables, ConfigType, &self.config, self.file_path);
|
||||||
self.dirty = false;
|
self.dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Guarda configuracion a archivo
|
/// Guarda configuracion a archivo
|
||||||
pub fn save(self: *Self) !void {
|
pub fn save(self: *Self, io: std.Io) !void {
|
||||||
try saveFn(variables, ConfigType, &self.config, self.file_path, app_name);
|
try saveFn(io, variables, ConfigType, &self.config, self.file_path, app_name);
|
||||||
self.dirty = false;
|
self.dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Carga configuracion o crea archivo con defaults si no existe
|
/// Carga configuracion o crea archivo con defaults si no existe
|
||||||
pub fn loadOrCreate(self: *Self) !void {
|
pub fn loadOrCreate(self: *Self, io: std.Io) !void {
|
||||||
self.load() catch |err| {
|
self.load(io) catch |err| {
|
||||||
if (err == error.FileNotFound) {
|
if (err == error.FileNotFound) {
|
||||||
// Archivo no existe - guardar defaults
|
// Archivo no existe - guardar defaults
|
||||||
try self.save();
|
try self.save(io);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
|
|
@ -918,7 +933,7 @@ const saveFn = save;
|
||||||
/// ```
|
/// ```
|
||||||
pub const FileWatcher = struct {
|
pub const FileWatcher = struct {
|
||||||
path: []const u8,
|
path: []const u8,
|
||||||
last_mtime: i128 = 0,
|
last_mtime: i96 = 0,
|
||||||
check_interval_ms: i64 = 1000,
|
check_interval_ms: i64 = 1000,
|
||||||
last_check: i64 = 0,
|
last_check: i64 = 0,
|
||||||
|
|
||||||
|
|
@ -935,8 +950,8 @@ pub const FileWatcher = struct {
|
||||||
/// Verifica si el archivo ha cambiado desde la ultima verificacion
|
/// Verifica si el archivo ha cambiado desde la ultima verificacion
|
||||||
/// Respeta el intervalo minimo entre verificaciones
|
/// Respeta el intervalo minimo entre verificaciones
|
||||||
/// @return true si el archivo cambio, false si no o si hubo error
|
/// @return true si el archivo cambio, false si no o si hubo error
|
||||||
pub fn checkForChanges(self: *FileWatcher) bool {
|
pub fn checkForChanges(self: *FileWatcher, io: std.Io) bool {
|
||||||
const now = std.time.milliTimestamp();
|
const now = currentMilliTimestamp();
|
||||||
|
|
||||||
// Respetar intervalo minimo
|
// Respetar intervalo minimo
|
||||||
if (now - self.last_check < self.check_interval_ms) {
|
if (now - self.last_check < self.check_interval_ms) {
|
||||||
|
|
@ -945,11 +960,11 @@ pub const FileWatcher = struct {
|
||||||
self.last_check = now;
|
self.last_check = now;
|
||||||
|
|
||||||
// Obtener mtime del archivo
|
// Obtener mtime del archivo
|
||||||
const stat = std.fs.cwd().statFile(self.path) catch {
|
const stat = std.Io.Dir.cwd().statFile(io, self.path, .{}) catch {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const file_mtime = stat.mtime;
|
const file_mtime = stat.mtime.nanoseconds;
|
||||||
|
|
||||||
// Primera vez - guardar mtime inicial
|
// Primera vez - guardar mtime inicial
|
||||||
if (self.last_mtime == 0) {
|
if (self.last_mtime == 0) {
|
||||||
|
|
@ -974,9 +989,9 @@ pub const FileWatcher = struct {
|
||||||
|
|
||||||
/// Fuerza actualizar el mtime sin detectar cambio
|
/// Fuerza actualizar el mtime sin detectar cambio
|
||||||
/// Util despues de guardar el archivo
|
/// Util despues de guardar el archivo
|
||||||
pub fn updateMtime(self: *FileWatcher) void {
|
pub fn updateMtime(self: *FileWatcher, io: std.Io) void {
|
||||||
const stat = std.fs.cwd().statFile(self.path) catch return;
|
const stat = std.Io.Dir.cwd().statFile(io, self.path, .{}) catch return;
|
||||||
self.last_mtime = stat.mtime;
|
self.last_mtime = stat.mtime.nanoseconds;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1206,7 +1221,7 @@ test "FileWatcher init" {
|
||||||
const watcher = FileWatcher.init("test.txt", 500);
|
const watcher = FileWatcher.init("test.txt", 500);
|
||||||
try std.testing.expectEqualStrings("test.txt", watcher.path);
|
try std.testing.expectEqualStrings("test.txt", watcher.path);
|
||||||
try std.testing.expectEqual(@as(i64, 500), watcher.check_interval_ms);
|
try std.testing.expectEqual(@as(i64, 500), watcher.check_interval_ms);
|
||||||
try std.testing.expectEqual(@as(i128, 0), watcher.last_mtime);
|
try std.testing.expectEqual(@as(i96, 0), watcher.last_mtime);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "loadFromString basic" {
|
test "loadFromString basic" {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue