From a5a505ecbaecacbc79c515daf55ea55ab8e475f0 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Sat, 24 Jul 2021 18:29:48 +0200 Subject: common: remove support for positional arguments This is currently unused and I don't like the approach anymore regardless. If/when we need positional arguments (probably when implementing the upcoming river-control protocol in rivertile) they should be handled separately from flags. This commit also improves the CLI error reporting to always print the usage string if invalid arguments were passed. --- common/args.zig | 126 ------------------------------------------------------- common/flags.zig | 104 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 126 deletions(-) delete mode 100644 common/args.zig create mode 100644 common/flags.zig (limited to 'common') diff --git a/common/args.zig b/common/args.zig deleted file mode 100644 index 669f9f9..0000000 --- a/common/args.zig +++ /dev/null @@ -1,126 +0,0 @@ -// This file is part of river, a dynamic tiling wayland compositor. -// -// Copyright 2021 The River Developers -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -const std = @import("std"); -const mem = std.mem; -const cstr = std.cstr; - -const root = @import("root"); - -pub const FlagDef = struct { - name: [*:0]const u8, - kind: enum { boolean, arg }, -}; - -pub fn Args(comptime num_positionals: comptime_int, comptime flag_defs: []const FlagDef) type { - return struct { - const Self = @This(); - - positionals: [num_positionals][*:0]const u8, - flags: [flag_defs.len]struct { - name: [*:0]const u8, - value: union { - boolean: bool, - arg: ?[*:0]const u8, - }, - }, - - pub fn parse(argv: [][*:0]const u8) Self { - var ret: Self = undefined; - - // Init all flags in the flags array to false/null - inline for (flag_defs) |flag_def, flag_idx| { - switch (flag_def.kind) { - .boolean => ret.flags[flag_idx] = .{ - .name = flag_def.name, - .value = .{ .boolean = false }, - }, - .arg => ret.flags[flag_idx] = .{ - .name = flag_def.name, - .value = .{ .arg = null }, - }, - } - } - - // Parse the argv in to the positionals and flags arrays - var arg_idx: usize = 0; - var positional_idx: usize = 0; - outer: while (arg_idx < argv.len) : (arg_idx += 1) { - var should_continue = false; - inline for (flag_defs) |flag_def, flag_idx| { - if (cstr.cmp(flag_def.name, argv[arg_idx]) == 0) { - switch (flag_def.kind) { - .boolean => ret.flags[flag_idx].value.boolean = true, - .arg => { - arg_idx += 1; - ret.flags[flag_idx].value.arg = if (arg_idx < argv.len) - argv[arg_idx] - else - root.fatal("flag '" ++ flag_def.name ++ - "' requires an argument but none was provided!", .{}); - }, - } - // TODO: this variable exists as a workaround for the fact that - // using continue :outer here crashes the stage1 compiler. - should_continue = true; - } - } - if (should_continue) continue; - - if (positional_idx == num_positionals) { - root.fatal( - "{} positional arguments expected but more were provided!", - .{num_positionals}, - ); - } - - // This check should not be needed as this code is unreachable - // if num_positionals is 0. Howevere the stage1 zig compiler does - // not seem to be smart enough to realize this. - if (num_positionals > 0) { - ret.positionals[positional_idx] = argv[arg_idx]; - } else { - unreachable; - } - positional_idx += 1; - } - - if (positional_idx < num_positionals) { - root.fatal( - "{} positional arguments expected but only {} were provided!", - .{ num_positionals, positional_idx }, - ); - } - - return ret; - } - - pub fn boolFlag(self: Self, flag_name: [*:0]const u8) bool { - for (self.flags) |flag| { - if (cstr.cmp(flag.name, flag_name) == 0) return flag.value.boolean; - } - unreachable; // Invalid flag_name - } - - pub fn argFlag(self: Self, flag_name: [*:0]const u8) ?[*:0]const u8 { - for (self.flags) |flag| { - if (cstr.cmp(flag.name, flag_name) == 0) return flag.value.arg; - } - unreachable; // Invalid flag_name - } - }; -} diff --git a/common/flags.zig b/common/flags.zig new file mode 100644 index 0000000..0836706 --- /dev/null +++ b/common/flags.zig @@ -0,0 +1,104 @@ +// This file is part of river, a dynamic tiling wayland compositor. +// +// Copyright 2021 The River Developers +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +const std = @import("std"); +const cstr = std.cstr; + +pub const Flag = struct { + name: [*:0]const u8, + kind: enum { boolean, arg }, +}; + +pub fn ParseResult(comptime flags: []const Flag) type { + return struct { + const Self = @This(); + + const FlagData = struct { + name: [*:0]const u8, + value: union { + boolean: bool, + arg: ?[*:0]const u8, + }, + }; + + /// Remaining args after the recognized flags + args: [][*:0]const u8, + /// Data obtained from parsed flags + flag_data: [flags.len]FlagData = blk: { + // Init all flags to false/null + var flag_data: [flags.len]FlagData = undefined; + inline for (flags) |flag, i| { + flag_data[i] = switch (flag.kind) { + .boolean => .{ + .name = flag.name, + .value = .{ .boolean = false }, + }, + .arg => .{ + .name = flag.name, + .value = .{ .arg = null }, + }, + }; + } + break :blk flag_data; + }, + + pub fn boolFlag(self: Self, flag_name: [*:0]const u8) bool { + for (self.flag_data) |flag_data| { + if (cstr.cmp(flag_data.name, flag_name) == 0) return flag_data.value.boolean; + } + unreachable; // Invalid flag_name + } + + pub fn argFlag(self: Self, flag_name: [*:0]const u8) ?[*:0]const u8 { + for (self.flag_data) |flag_data| { + if (cstr.cmp(flag_data.name, flag_name) == 0) return flag_data.value.arg; + } + unreachable; // Invalid flag_name + } + }; +} + +pub fn parse(args: [][*:0]const u8, comptime flags: []const Flag) !ParseResult(flags) { + var ret: ParseResult(flags) = .{ .args = undefined }; + + var arg_idx: usize = 0; + while (arg_idx < args.len) : (arg_idx += 1) { + var parsed_flag = false; + inline for (flags) |flag, flag_idx| { + if (cstr.cmp(flag.name, args[arg_idx]) == 0) { + switch (flag.kind) { + .boolean => ret.flag_data[flag_idx].value.boolean = true, + .arg => { + arg_idx += 1; + if (arg_idx == args.len) { + std.log.err("flag '" ++ flag.name ++ + "' requires an argument but none was provided!", .{}); + return error.MissingFlagArgument; + } + ret.flag_data[flag_idx].value.arg = args[arg_idx]; + }, + } + parsed_flag = true; + } + } + if (!parsed_flag) break; + } + + ret.args = args[arg_idx..]; + + return ret; +} -- cgit v1.2.3