182 lines
5.1 KiB
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());
|
|
}
|
|
}
|
|
}
|