152 lines
5.1 KiB
Zig
152 lines
5.1 KiB
Zig
const std = @import("std");
|
|
const types = @import("types.zig");
|
|
const print = std.debug.print;
|
|
|
|
pub const HELP_TEXT =
|
|
\\Usage: manwhere [OPTIONS] <keyword>
|
|
\\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);
|
|
}
|