diff options
Diffstat (limited to 'src/cmd/lib/flags.zig')
-rw-r--r-- | src/cmd/lib/flags.zig | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/src/cmd/lib/flags.zig b/src/cmd/lib/flags.zig new file mode 100644 index 0000000..637ea83 --- /dev/null +++ b/src/cmd/lib/flags.zig @@ -0,0 +1,197 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const ArrayList = std.ArrayList; +const HashMap = std.HashMap; + +pub const FlagType = enum { + Boolean, + String, + Number, +}; + +pub const FlagDef = struct { + name: []const u8, + aliases: []const []const u8, + flag_type: FlagType, + description: []const u8, + default_value: ?[]const u8 = null, +}; + +pub const FlagValue = union(FlagType) { + Boolean: bool, + String: []const u8, + Number: i32, +}; + +pub const ParsedFlags = struct { + values: HashMap([]const u8, FlagValue, StringContext, std.hash_map.default_max_load_percentage), + allocator: Allocator, + + const StringContext = struct { + pub fn hash(self: @This(), s: []const u8) u64 { + _ = self; + return std.hash_map.hashString(s); + } + pub fn eql(self: @This(), a: []const u8, b: []const u8) bool { + _ = self; + return std.mem.eql(u8, a, b); + } + }; + + pub fn init(allocator: Allocator) ParsedFlags { + return ParsedFlags{ + .values = HashMap([]const u8, FlagValue, StringContext, std.hash_map.default_max_load_percentage).init(allocator), + .allocator = allocator, + }; + } + + pub fn deinit(self: *ParsedFlags) void { + self.values.deinit(); + } + + pub fn getBool(self: *const ParsedFlags, name: []const u8) bool { + if (self.values.get(name)) |value| { + return switch (value) { + .Boolean => |b| b, + else => false, + }; + } + return false; + } + + pub fn getString(self: *const ParsedFlags, name: []const u8) ?[]const u8 { + if (self.values.get(name)) |value| { + return switch (value) { + .String => |s| s, + else => null, + }; + } + return null; + } + + pub fn getNumber(self: *const ParsedFlags, name: []const u8) ?i32 { + if (self.values.get(name)) |value| { + return switch (value) { + .Number => |n| n, + else => null, + }; + } + return null; + } + + pub fn setValue(self: *ParsedFlags, name: []const u8, value: FlagValue) !void { + try self.values.put(name, value); + } +}; + +pub const CommandFlags = struct { + flags: []const FlagDef, + + pub fn parse(self: *const CommandFlags, args: []const []const u8, allocator: Allocator) !ParsedFlags { + var parsed = ParsedFlags.init(allocator); + + for (args) |arg| { + if (arg.len == 0 or arg[0] != '/') continue; + + const flag_text = arg[1..]; + if (flag_text.len == 0) continue; + + var flag_name: []const u8 = undefined; + var flag_value: ?[]const u8 = null; + + if (std.mem.indexOf(u8, flag_text, ":")) |colon_pos| { + flag_name = flag_text[0..colon_pos]; + flag_value = flag_text[colon_pos + 1 ..]; + } else { + flag_name = flag_text; + } + + const flag_def = self.findFlag(flag_name) orelse { + return error.UnknownFlag; + }; + + const value = switch (flag_def.flag_type) { + .Boolean => FlagValue{ .Boolean = true }, + .String => blk: { + const str_value = flag_value orelse flag_def.default_value orelse { + return error.MissingFlagValue; + }; + break :blk FlagValue{ .String = try allocator.dupe(u8, str_value) }; + }, + .Number => blk: { + const str_value = flag_value orelse flag_def.default_value orelse { + return error.MissingFlagValue; + }; + const num_value = std.fmt.parseInt(i32, str_value, 10) catch { + return error.InvalidNumber; + }; + break :blk FlagValue{ .Number = num_value }; + }, + }; + + try parsed.setValue(flag_def.name, value); + } + + return parsed; + } + + fn findFlag(self: *const CommandFlags, name: []const u8) ?*const FlagDef { + for (self.flags) |*flag_def| { + if (std.mem.eql(u8, flag_def.name, name)) { + return flag_def; + } + + for (flag_def.aliases) |alias| { + if (alias.len > 0 and alias[0] == '/') { + if (std.mem.eql(u8, alias[1..], name)) { + return flag_def; + } + } else if (std.mem.eql(u8, alias, name)) { + return flag_def; + } + } + } + return null; + } + + pub fn getHelp(self: *const CommandFlags, command_name: []const u8, allocator: Allocator) ![]const u8 { + var help = ArrayList(u8).init(allocator); + defer help.deinit(); + + try help.appendSlice(command_name); + try help.appendSlice(" - Available flags:\n\n"); + + for (self.flags) |flag_def| { + try help.appendSlice(" /"); + try help.appendSlice(flag_def.name); + + if (flag_def.aliases.len > 0) { + try help.appendSlice(" ("); + for (flag_def.aliases, 0..) |alias, i| { + if (i > 0) try help.appendSlice(", "); + try help.appendSlice(alias); + } + try help.appendSlice(")"); + } + + switch (flag_def.flag_type) { + .String => try help.appendSlice(":value"), + .Number => try help.appendSlice(":number"), + .Boolean => {}, + } + + try help.appendSlice(" - "); + try help.appendSlice(flag_def.description); + try help.appendSlice("\n"); + } + + return help.toOwnedSlice(); + } +}; + +pub const FlagParseError = error{ + UnknownFlag, + MissingFlagValue, + InvalidNumber, + OutOfMemory, +}; |