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 "", }); } else { const res = StringPacket{ .str = "" }; try res.write(con.stream.writer()); } } }