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,
|
||||
.version = "0.1.0",
|
||||
.minimum_zig_version = "0.15.0",
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.fingerprint = 0x6211a84c80e77eb,
|
||||
|
||||
.dependencies = .{},
|
||||
|
|
|
|||
|
|
@ -460,9 +460,15 @@ fn parseBool(value: []const u8) ?bool {
|
|||
// 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.
|
||||
/// Requiere que ConfigType tenga un campo `allocator`.
|
||||
pub fn load(
|
||||
io: std.Io,
|
||||
comptime variables: []const ConfigVariable,
|
||||
comptime ConfigType: type,
|
||||
config: *ConfigType,
|
||||
|
|
@ -470,9 +476,13 @@ pub fn load(
|
|||
) !void {
|
||||
const EngineType = Engine(variables, ConfigType);
|
||||
|
||||
// Leer archivo completo (propaga FileNotFound para que loadOrCreate pueda crear)
|
||||
const content = try std.fs.cwd().readFileAlloc(config.allocator, path, 1024 * 1024);
|
||||
// Leer archivo completo
|
||||
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);
|
||||
_ = try file.readPositional(io, &.{content}, 0);
|
||||
|
||||
// Parsear linea por linea
|
||||
var lines = std.mem.splitScalar(u8, content, '\n');
|
||||
|
|
@ -522,6 +532,7 @@ fn findCommentStart(text: []const u8) ?usize {
|
|||
|
||||
/// Guarda configuracion a archivo
|
||||
pub fn save(
|
||||
io: std.Io,
|
||||
comptime variables: []const ConfigVariable,
|
||||
comptime ConfigType: type,
|
||||
config: *const ConfigType,
|
||||
|
|
@ -530,20 +541,17 @@ pub fn save(
|
|||
) !void {
|
||||
const EngineType = Engine(variables, ConfigType);
|
||||
|
||||
const file = try std.fs.cwd().createFile(path, .{});
|
||||
defer file.close();
|
||||
const file = try std.Io.Dir.cwd().createFile(io, path, .{});
|
||||
defer file.close(io);
|
||||
|
||||
// Header del archivo
|
||||
try file.writeAll("# ============================================================================\n");
|
||||
try file.writeStreamingAll(io, "# ============================================================================\n");
|
||||
var header_buf: [128]u8 = undefined;
|
||||
const header_line = std.fmt.bufPrint(&header_buf, "# {s} - Archivo de Configuracion\n", .{app_name}) catch "";
|
||||
try file.writeAll(header_line);
|
||||
try file.writeAll("# ============================================================================\n");
|
||||
try file.writeAll("# \n");
|
||||
try file.writeAll("# Este archivo se genera automaticamente. Puedes editarlo manualmente.\n");
|
||||
try file.writeAll("# Formato: @variable: valor # descripcion\n");
|
||||
try file.writeAll("# \n");
|
||||
try file.writeAll("\n");
|
||||
try file.writeStreamingAll(io, header_line);
|
||||
try file.writeStreamingAll(io, "# ============================================================================\n");
|
||||
try file.writeStreamingAll(io, "# \n");
|
||||
try file.writeAll(io, &.{ "# Este archivo se genera automaticamente. Puedes editarlo manualmente.\n", "# Formato: @variable: valor # descripcion\n", "# \n", "\n" });
|
||||
|
||||
var current_category: ?usize = null;
|
||||
var value_buf: [256]u8 = undefined;
|
||||
|
|
@ -556,13 +564,13 @@ pub fn save(
|
|||
// Header de categoria si cambio
|
||||
if (current_category == null or current_category.? != v.category) {
|
||||
if (current_category != null) {
|
||||
try file.writeAll("\n");
|
||||
try file.writeStreamingAll(io, "\n");
|
||||
}
|
||||
|
||||
// Si la variable tiene header personalizado, usarlo
|
||||
if (v.category_header) |header| {
|
||||
try file.writeAll(header);
|
||||
try file.writeAll("\n");
|
||||
try file.writeStreamingAll(io, header);
|
||||
try file.writeStreamingAll(io, "\n");
|
||||
}
|
||||
|
||||
current_category = v.category;
|
||||
|
|
@ -591,7 +599,7 @@ pub fn save(
|
|||
// @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);
|
||||
try file.writeStreamingAll(io, desc_text);
|
||||
|
||||
// Parsear opciones con descripciones
|
||||
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_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);
|
||||
try file.writeStreamingAll(io, opt_line);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -609,7 +617,7 @@ pub fn save(
|
|||
v.config_key,
|
||||
value,
|
||||
}) catch unreachable;
|
||||
try file.writeAll(line);
|
||||
try file.writeStreamingAll(io, line);
|
||||
} else {
|
||||
// Formato simple: comentario inline con opciones entre corchetes
|
||||
var comment_buf: [256]u8 = undefined;
|
||||
|
|
@ -626,21 +634,21 @@ pub fn save(
|
|||
spaces(padding),
|
||||
comment,
|
||||
}) catch unreachable;
|
||||
try file.writeAll(line);
|
||||
try file.writeStreamingAll(io, line);
|
||||
} else {
|
||||
const line = std.fmt.bufPrint(&line_buf, "{s}: {s}\n", .{
|
||||
v.config_key,
|
||||
value,
|
||||
}) catch unreachable;
|
||||
try file.writeAll(line);
|
||||
try file.writeStreamingAll(io, line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try file.writeAll("\n");
|
||||
try file.writeAll("# ============================================================================\n");
|
||||
try file.writeAll("# Fin del archivo de configuracion\n");
|
||||
try file.writeAll("# ============================================================================\n");
|
||||
try file.writeStreamingAll(io, "\n");
|
||||
try file.writeStreamingAll(io, "# ============================================================================\n");
|
||||
try file.writeStreamingAll(io, "# Fin del archivo de configuracion\n");
|
||||
try file.writeStreamingAll(io, "# ============================================================================\n");
|
||||
}
|
||||
|
||||
/// Genera string de N espacios (maximo 64)
|
||||
|
|
@ -649,13 +657,19 @@ fn spaces(n: usize) []const u8 {
|
|||
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
|
||||
pub fn createBackup(path: []const u8) !void {
|
||||
const now = std.time.timestamp();
|
||||
pub fn createBackup(io: std.Io, path: []const u8) !void {
|
||||
const now = currentTimestamp();
|
||||
var backup_path_buf: [512]u8 = undefined;
|
||||
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;
|
||||
return err;
|
||||
};
|
||||
|
|
@ -720,6 +734,7 @@ pub fn ConfigManager(
|
|||
/// - Crea Config con valores por defecto
|
||||
/// - Llama loadOrCreate() automaticamente
|
||||
pub fn init(
|
||||
io: std.Io,
|
||||
allocator: std.mem.Allocator,
|
||||
file_path: []const u8,
|
||||
) !Self {
|
||||
|
|
@ -732,7 +747,7 @@ pub fn ConfigManager(
|
|||
};
|
||||
|
||||
// Cargar o crear archivo automaticamente
|
||||
try self.loadOrCreate();
|
||||
try self.loadOrCreate(io);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
|
@ -756,10 +771,10 @@ pub fn ConfigManager(
|
|||
}
|
||||
|
||||
/// 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
|
||||
if (self.dirty) {
|
||||
self.save() catch {};
|
||||
self.save(io) catch {};
|
||||
}
|
||||
|
||||
// Liberar config
|
||||
|
|
@ -834,23 +849,23 @@ pub fn ConfigManager(
|
|||
}
|
||||
|
||||
/// Carga configuracion desde archivo
|
||||
pub fn load(self: *Self) !void {
|
||||
try loadFn(variables, ConfigType, &self.config, self.file_path);
|
||||
pub fn load(self: *Self, io: std.Io) !void {
|
||||
try loadFn(io, variables, ConfigType, &self.config, self.file_path);
|
||||
self.dirty = false;
|
||||
}
|
||||
|
||||
/// Guarda configuracion a archivo
|
||||
pub fn save(self: *Self) !void {
|
||||
try saveFn(variables, ConfigType, &self.config, self.file_path, app_name);
|
||||
pub fn save(self: *Self, io: std.Io) !void {
|
||||
try saveFn(io, variables, ConfigType, &self.config, self.file_path, app_name);
|
||||
self.dirty = false;
|
||||
}
|
||||
|
||||
/// Carga configuracion o crea archivo con defaults si no existe
|
||||
pub fn loadOrCreate(self: *Self) !void {
|
||||
self.load() catch |err| {
|
||||
pub fn loadOrCreate(self: *Self, io: std.Io) !void {
|
||||
self.load(io) catch |err| {
|
||||
if (err == error.FileNotFound) {
|
||||
// Archivo no existe - guardar defaults
|
||||
try self.save();
|
||||
try self.save(io);
|
||||
return;
|
||||
}
|
||||
return err;
|
||||
|
|
@ -918,7 +933,7 @@ const saveFn = save;
|
|||
/// ```
|
||||
pub const FileWatcher = struct {
|
||||
path: []const u8,
|
||||
last_mtime: i128 = 0,
|
||||
last_mtime: i96 = 0,
|
||||
check_interval_ms: i64 = 1000,
|
||||
last_check: i64 = 0,
|
||||
|
||||
|
|
@ -935,8 +950,8 @@ pub const FileWatcher = struct {
|
|||
/// Verifica si el archivo ha cambiado desde la ultima verificacion
|
||||
/// Respeta el intervalo minimo entre verificaciones
|
||||
/// @return true si el archivo cambio, false si no o si hubo error
|
||||
pub fn checkForChanges(self: *FileWatcher) bool {
|
||||
const now = std.time.milliTimestamp();
|
||||
pub fn checkForChanges(self: *FileWatcher, io: std.Io) bool {
|
||||
const now = currentMilliTimestamp();
|
||||
|
||||
// Respetar intervalo minimo
|
||||
if (now - self.last_check < self.check_interval_ms) {
|
||||
|
|
@ -945,11 +960,11 @@ pub const FileWatcher = struct {
|
|||
self.last_check = now;
|
||||
|
||||
// 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;
|
||||
};
|
||||
|
||||
const file_mtime = stat.mtime;
|
||||
const file_mtime = stat.mtime.nanoseconds;
|
||||
|
||||
// Primera vez - guardar mtime inicial
|
||||
if (self.last_mtime == 0) {
|
||||
|
|
@ -974,9 +989,9 @@ pub const FileWatcher = struct {
|
|||
|
||||
/// Fuerza actualizar el mtime sin detectar cambio
|
||||
/// Util despues de guardar el archivo
|
||||
pub fn updateMtime(self: *FileWatcher) void {
|
||||
const stat = std.fs.cwd().statFile(self.path) catch return;
|
||||
self.last_mtime = stat.mtime;
|
||||
pub fn updateMtime(self: *FileWatcher, io: std.Io) void {
|
||||
const stat = std.Io.Dir.cwd().statFile(io, self.path, .{}) catch return;
|
||||
self.last_mtime = stat.mtime.nanoseconds;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -1206,7 +1221,7 @@ test "FileWatcher init" {
|
|||
const watcher = FileWatcher.init("test.txt", 500);
|
||||
try std.testing.expectEqualStrings("test.txt", watcher.path);
|
||||
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" {
|
||||
|
|
|
|||
Loading…
Reference in a new issue