const std = @import("std"); const types = @import("types.zig"); const print = std.debug.print; pub const HELP_TEXT = \\Usage: manwhere [OPTIONS] \\Find man pages containing a keyword. \\ \\OPTIONS: \\ -h, --help Show this help message \\ -s, --section NUM Only show results from section NUM (1-9) \\ \\EXAMPLES: \\ manwhere sleep # find all man pages mentioning "sleep" \\ manwhere sleep | fzf | xargs man # pipe to fzf for interactive selection \\ manwhere -s 1 sleep # find only in section 1 \\ ; pub fn parseArgs(allocator: std.mem.Allocator, args: [][:0]u8) !types.SearchConfig { if (args.len < 2) { print("{s}", .{HELP_TEXT}); return error.NoKeyword; } var keyword: []const u8 = ""; var sections_list = std.ArrayList([]const u8){}; defer sections_list.deinit(allocator); var i: usize = 1; while (i < args.len) : (i += 1) { const arg = args[i]; if (std.mem.eql(u8, arg, "-h") or std.mem.eql(u8, arg, "--help")) { print("{s}", .{HELP_TEXT}); return error.HelpRequested; } else if (std.mem.eql(u8, arg, "-s") or std.mem.eql(u8, arg, "--section")) { if (i + 1 >= args.len) { print("[X] Option {s} requires a section number\n", .{arg}); return error.InvalidOption; } i += 1; const section_arg = args[i]; if (section_arg.len == 0 or section_arg[0] < '1' or section_arg[0] > '9') { print("[X] Invalid section '{s}'\n", .{section_arg}); return error.InvalidSection; } try sections_list.append(allocator, section_arg); } else if (std.mem.startsWith(u8, arg, "-")) { print("[X] Unknown option: {s}\n", .{arg}); return error.InvalidOption; } else if (keyword.len == 0) { keyword = arg; } else { print("[X] Multiple keywords not supported\n", .{}); return error.MultipleKeywords; } } if (keyword.len == 0) { print("[X] No keyword provided\n", .{}); return error.NoKeyword; } const target_sections = if (sections_list.items.len > 0) try sections_list.toOwnedSlice(allocator) else null; return types.SearchConfig{ .keyword = keyword, .target_sections = target_sections, }; } // ============================================================================ // TESTS // ============================================================================ test "parseArgs - basic keyword" { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); const allocator = gpa.allocator(); const args = &[_][:0]u8{ "manwhere", "sleep" }; const config = try parseArgs(allocator, args); try std.testing.expectEqualStrings("sleep", config.keyword); try std.testing.expect(config.target_sections == null); } test "parseArgs - with section flag" { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); const allocator = gpa.allocator(); const args = &[_][:0]u8{ "manwhere", "-s", "1", "ls" }; const config = try parseArgs(allocator, args); try std.testing.expectEqualStrings("ls", config.keyword); try std.testing.expect(config.target_sections != null); try std.testing.expectEqual(@as(usize, 1), config.target_sections.?.len); try std.testing.expectEqualStrings("1", config.target_sections.?[0]); } test "parseArgs - with multiple sections" { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); const allocator = gpa.allocator(); const args = &[_][:0]u8{ "manwhere", "-s", "1", "-s", "3", "test" }; const config = try parseArgs(allocator, args); try std.testing.expectEqualStrings("test", config.keyword); try std.testing.expect(config.target_sections != null); try std.testing.expectEqual(@as(usize, 2), config.target_sections.?.len); try std.testing.expectEqualStrings("1", config.target_sections.?[0]); try std.testing.expectEqualStrings("3", config.target_sections.?[1]); } test "parseArgs - error no keyword" { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); const allocator = gpa.allocator(); const args = &[_][:0]u8{"manwhere"}; const result = parseArgs(allocator, args); try std.testing.expectError(error.NoKeyword, result); } test "parseArgs - error invalid option" { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); const allocator = gpa.allocator(); const args = &[_][:0]u8{ "manwhere", "--invalid", "ls" }; const result = parseArgs(allocator, args); try std.testing.expectError(error.InvalidOption, result); } test "parseArgs - error multiple keywords" { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); const allocator = gpa.allocator(); const args = &[_][:0]u8{ "manwhere", "ls", "cat" }; const result = parseArgs(allocator, args); try std.testing.expectError(error.MultipleKeywords, result); }