aboutsummaryrefslogtreecommitdiff
path: root/riverctl/args.zig
diff options
context:
space:
mode:
authorIsaac Freund <ifreund@ifreund.xyz>2021-01-17 16:30:47 +0100
committerIsaac Freund <ifreund@ifreund.xyz>2021-01-18 22:30:52 +0100
commit18bab45d4c12f64c3c28b07243c79e807fe45ccd (patch)
tree66b7c8dc19648280d7447ae5126c24964af07104 /riverctl/args.zig
parent421c403cf50662aa0b4f02241ab727c41b30fa30 (diff)
downloadriver-18bab45d4c12f64c3c28b07243c79e807fe45ccd.tar.gz
river-18bab45d4c12f64c3c28b07243c79e807fe45ccd.tar.xz
riverctl: implement river-options interface
To make this cleaner, introduce some arg-parsing infrastructure that will useful when porting riverctl to river-control-v2 in the future as well.
Diffstat (limited to 'riverctl/args.zig')
-rw-r--r--riverctl/args.zig115
1 files changed, 115 insertions, 0 deletions
diff --git a/riverctl/args.zig b/riverctl/args.zig
new file mode 100644
index 0000000..3240a97
--- /dev/null
+++ b/riverctl/args.zig
@@ -0,0 +1,115 @@
+// 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 <https://www.gnu.org/licenses/>.
+
+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) {
+ 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.printErrorExit("flag '" ++ flag_def.name ++
+ "' requires an argument but none was provided!", .{});
+ },
+ }
+ continue :outer;
+ }
+ }
+
+ if (positional_idx == num_positionals) {
+ root.printErrorExit(
+ "{} positional arguments expected but more were provided!",
+ .{num_positionals},
+ );
+ }
+
+ ret.positionals[positional_idx] = argv[arg_idx];
+ positional_idx += 1;
+ }
+
+ if (positional_idx < num_positionals) {
+ root.printErrorExit(
+ "{} 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;
+ }
+
+ 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;
+ }
+ };
+}