river/riverctl/args.zig

116 lines
4.1 KiB
Zig
Raw Normal View History

// 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;
}
};
}