const std = @import("std"); const ascii = std.ascii; const mem = std.mem; const os = std.os; const fs = std.fs; const Uri = std.Uri; const csv = @import("csv.zig"); const dir = "/root/yujiri.xyz/content/thoya/search"; pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; var arena = std.heap.ArenaAllocator.init(gpa.allocator()); defer arena.deinit(); const stdout = std.io.getStdOut().writer(); const query_encoded = os.getenv("QUERY_STRING") orelse ""; if (query_encoded.len == 0) { try stdout.print("10 Enter your query\r\n", .{}); os.exit(0); } var query = try Uri.unescapeString(arena.allocator(), query_encoded); try stdout.print("20 text/gemini; lang=en\r\n", .{}); const file = try fs.cwd().openFile(dir ++ "/../dict.csv", .{}); var records = csv.CSVReader.new(file, arena.allocator()); var foundAny = false; while (try records.next()) |record| { if (matches(record, query)) { foundAny = true; try stdout.print( \\### {s} \\Type: {s} \\Meaning: {s} \\Translations: {s} \\ \\ , .{ record[0], record[1], record[2], record[3] }); if (record[4].len > 0) try stdout.print("{s}\n\n", .{record[4]}); } } if (!foundAny) try stdout.print("No results found.", .{}); } fn matches(record: [][]u8, query: []const u8) bool { const exePath = mem.sliceTo(os.argv[0], 0); var iter = mem.split(u8, exePath, "/"); var queryType: []const u8 = undefined; while (iter.next()) |segment| queryType = segment; if (mem.eql(u8, queryType, "word")) { return mem.eql(u8, record[0], query); } if (mem.eql(u8, queryType, "translations")) { var translations = mem.split(u8, record[3], ", "); while (translations.next()) |t| { if (mem.eql(u8, t, query)) return true; } return false; } if (mem.eql(u8, queryType, "notes")) { const notes = record[4]; var queryWords = mem.split(u8, query, " "); // Check if all of the words are found. queryWord: while (queryWords.next()) |queryWord| { var notesWords = mem.tokenize(u8, notes, " `~!@#$%^&*()-_=+[]{}\\|;:'\",.<>/?"); while (notesWords.next()) |notesWord| { // Query word fuond, go check the next one. if (ascii.eqlIgnoreCase(notesWord, queryWord)) continue :queryWord; } // Query word not found; this is a not a match. return false; } return true; } unreachable; }