Files
zocket/src/main.zig

94 lines
3.0 KiB
Zig

//! Entry point for the zocket daemon.
//!
//! Handles allocator selection (debug-checked vs. release), CLI argument
//! parsing, and configuration path resolution before handing off to `run`.
const zocket = @import("zocket");
const std = @import("std");
const builtin = @import("builtin");
const options = @import("build_options");
const args = zocket.deps.args;
/// Resolves the config path, then delegates to `run`.
///
/// Allocator strategy:
/// - Debug builds use `DebugAllocator`, which tracks every allocation and
/// panics/exits on leaks or double-frees.
/// - Release builds use `smp_allocator`.
pub fn main(init: std.process.Init) !void
{
const debug = builtin.mode == .Debug;
const io = init.io;
var gpa: std.heap.DebugAllocator(.{}) = .init;
defer if (gpa.deinit() == .leak) std.process.exit(1);
const allocator = if (debug) gpa.allocator() else std.heap.smp_allocator;
var tmp_arena: ?std.heap.ArenaAllocator = .init(allocator);
errdefer _ = zocket.memory.deinitIfExists(&tmp_arena);
const tmp_allocator = tmp_arena.?.allocator();
var parser = try args.ArgumentParser.init(tmp_allocator, .{
.name = "zocket",
.version = options.version,
.description = "A lightweight Zig WebSocket server and mesh routing daemon.",
});
try parser.addOption("path", .{
.short = 'p',
.help = "Configuration-File Path (*.toml)",
});
var result = try parser.parseProcess(init);
const path = if (debug) path:
{
const tmp_path = result.getString("path") orelse "config/config.toml";
break :path try allocator.dupe(u8, tmp_path);
}
else path:
{
const tmp_path = result.getString("path") orelse blk: {
const exe = try std.process.executableDirPathAlloc(io, tmp_allocator);
break :blk try std.Io.Dir.path.join(tmp_allocator, &.{ exe, "config/config.toml" });
};
break :path try allocator.dupe(u8, tmp_path);
};
_ = zocket.memory.deinitIfExists(&tmp_arena);
try run(io, allocator, path);
}
/// Loads and parses the configuration file at `path`, then hands off to
/// the rest of the application.
///
/// Owns `path` and frees it once it's no longer needed; Callers must not free `path` themselves!
///
/// On parse failure, logs a human-readable reason and terminates the
/// process immediately via `std.process.exit`.
fn run(io: std.Io, allocator: std.mem.Allocator, path: []const u8) !void
{
const parsed = zocket.config.parse(io, allocator, path) catch |err|
{
const msg: []const u8 = switch (err) {
error.FileNotFound => "File not found",
error.AccessDenied => "Permission denied reading",
else => "Unexpected error opening",
};
std.log.err("{s}: '{s}' ({any})", .{ msg, path, err });
std.process.exit(1);
};
defer parsed.deinit();
allocator.free(path);
const configuration = parsed.value;
std.debug.print("{any}", .{configuration});
}