portingtools/src/main.zig

182 lines
5.1 KiB
Zig

const std = @import("std");
const csv_reader = @import("csv_reader.zig");
const StringPacket = @import("StringPacket.zig");
pub const std_options = struct {
pub const log_level = .debug;
};
const Mapping = struct {
mapped: []const u8,
doc: ?[]const u8,
};
fn getAddr(alloc: std.mem.Allocator) !std.net.Address {
const sockpath = try std.fs.path.join(alloc, &.{
std.os.getenv("XDG_RUNTIME_DIR") orelse return error.MissingRuntimeDir,
"portingtools.sock",
});
defer alloc.free(sockpath);
return try std.net.Address.initUnix(sockpath);
}
pub fn main() !void {
if (std.os.argv.len < 2) {
return error.InvalidArgs;
}
const cmd = std.mem.span(std.os.argv[1]);
if (std.mem.eql(u8, cmd, "serve")) {
try runServer();
} else if (std.mem.eql(u8, cmd, "map")) {
try map();
} else {
return error.InvalidCommand;
}
}
fn map() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const alloc = gpa.allocator();
const stdin = std.io.getStdIn().reader();
var buf: [128]u8 = undefined;
const request = (try stdin.readUntilDelimiterOrEof(&buf, '\n')) orelse return error.NoInput;
const addr = try getAddr(alloc);
const sockfd = try std.os.socket(
std.os.AF.UNIX,
std.os.SOCK.STREAM | std.os.SOCK.CLOEXEC,
0,
);
defer std.os.closeSocket(sockfd);
try std.os.connect(sockfd, &addr.any, addr.getOsSockLen());
const stream = std.net.Stream{ .handle = sockfd };
const pkt = StringPacket{ .str = std.mem.trim(u8, request, &std.ascii.whitespace) };
try pkt.write(stream.writer());
const res = try StringPacket.read(stream.reader(), alloc);
defer alloc.free(res.str);
try std.io.getStdOut().writer().print("{s}\n", .{res.str});
}
fn runServer() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const alloc = gpa.allocator();
var mappings = std.StringHashMap(Mapping).init(alloc);
defer mappings.deinit();
var data_arena = std.heap.ArenaAllocator.init(alloc);
defer data_arena.deinit();
var renames = std.StringHashMap([]const u8).init(alloc);
defer renames.deinit();
if (std.fs.cwd().openFile("renames.csv", .{})) |renames_file| {
defer renames_file.close();
var reader = csv_reader.csvReader(std.io.bufferedReader(renames_file.reader()));
while (try reader.next(data_arena.allocator())) |rec| {
if (rec.cols.len != 2) {
std.log.warn("found rename with invalid record length, skipping", .{});
continue;
}
if (renames.contains(rec.cols[0])) {
std.log.warn("duplicate rename '{s}'", .{rec.cols[0]});
continue;
}
try renames.put(rec.cols[0], rec.cols[1]);
}
std.log.info("loaded {} renames", .{renames.count()});
} else |err| {
std.log.warn("couldn't open renames file: {}, skipping", .{err});
}
var mappings_dir = try std.fs.cwd().openIterableDir("mappings", .{});
defer mappings_dir.close();
var mappings_iter = mappings_dir.iterate();
while (try mappings_iter.next()) |entry| {
const fpath = try std.fs.path.join(alloc, &.{ "mappings", entry.name });
defer alloc.free(fpath);
var mapfile = try std.fs.cwd().openFile(fpath, .{});
defer mapfile.close();
var reader = csv_reader.csvReader(std.io.bufferedReader(mapfile.reader()));
while (try reader.next(data_arena.allocator())) |rec| {
if (rec.cols.len < 2) {
std.log.warn("found mapping with invalid length, skipping", .{});
continue;
}
if (mappings.contains(rec.cols[0])) {
std.log.warn("duplicate mapping '{s}'", .{rec.cols[0]});
continue;
}
const mapping = Mapping{
.mapped = rec.cols[1],
.doc = if (rec.cols.len >= 4) rec.cols[3] else null,
};
try mappings.put(rec.cols[0], mapping);
}
}
std.log.info("loaded {} mappings", .{mappings.count()});
var server = std.net.StreamServer.init(.{});
defer server.deinit();
const addr = try getAddr(alloc);
try server.listen(addr);
std.log.info("listening on {}", .{addr});
while (true) {
const con = try server.accept();
const req = try StringPacket.read(con.stream.reader(), alloc);
defer alloc.free(req.str);
if (mappings.get(req.str)) |mapping| {
const res = StringPacket{ .str = mapping.mapped };
try res.write(con.stream.writer());
std.log.info(
\\
\\ Unmapped: {s}
\\ Mapped: {s}
\\
\\ Doc: {s}
, .{
req.str,
mapping.mapped,
mapping.doc orelse "<no docs>",
});
} else {
const res = StringPacket{ .str = "<unknown>" };
try res.write(con.stream.writer());
}
}
}