summaryrefslogtreecommitdiff
path: root/src/cli/args.zig
diff options
context:
space:
mode:
authorMitchell Hashimoto <m@mitchellh.com>2024-10-18 12:53:32 -0700
committerMitchell Hashimoto <m@mitchellh.com>2024-10-18 12:59:16 -0700
commitc90ed293417add9bab0ac52ccef9eae0efd3e0cb (patch)
treedebc3a13cafcb2d1df9f9b5557147dad468c2000 /src/cli/args.zig
parent98a7573ed4c2b563f3848e0d6be691f1cd173ade (diff)
cli: skip argv0 and actions when parsing CLI flags
This fixes a regression from #2454. In that PR, we added an error when positional arguments are detected. I believe that's correct, but we were silently relying on the previous behavior in the CLI commands. This commit changes the CLI commands to use a new argsIterator function that creates an iterator that skips the first argument (argv0). This is the same behavior that the config parsing does and now uses this shared logic. This also makes it so the argsIterator ignores actions (`+things`) and we document that we expect those to be handled earlier.
Diffstat (limited to 'src/cli/args.zig')
-rw-r--r--src/cli/args.zig41
1 files changed, 41 insertions, 0 deletions
diff --git a/src/cli/args.zig b/src/cli/args.zig
index 3dcc08dac..bfd40c633 100644
--- a/src/cli/args.zig
+++ b/src/cli/args.zig
@@ -4,6 +4,7 @@ const assert = std.debug.assert;
const Allocator = mem.Allocator;
const ArenaAllocator = std.heap.ArenaAllocator;
const diags = @import("diagnostics.zig");
+const internal_os = @import("../os/main.zig");
const Diagnostic = diags.Diagnostic;
const DiagnosticList = diags.DiagnosticList;
@@ -894,6 +895,9 @@ test "parseIntoField: tagged union missing tag" {
/// An iterator that considers its location to be CLI args. It
/// iterates through an underlying iterator and increments a counter
/// to track the current CLI arg index.
+///
+/// This also ignores any argument that starts with `+`. It assumes that
+/// actions were parsed out before this iterator was created.
pub fn ArgsIterator(comptime Iterator: type) type {
return struct {
const Self = @This();
@@ -906,9 +910,21 @@ pub fn ArgsIterator(comptime Iterator: type) type {
/// values yet.
index: usize = 0,
+ pub fn deinit(self: *Self) void {
+ if (@hasDecl(Iterator, "deinit")) {
+ self.iterator.deinit();
+ }
+ }
+
pub fn next(self: *Self) ?[]const u8 {
const value = self.iterator.next() orelse return null;
self.index += 1;
+
+ // We ignore any argument that starts with "+". This is used
+ // to indicate actions and are expected to be parsed out before
+ // this iterator is created.
+ if (value.len > 0 and value[0] == '+') return self.next();
+
return value;
}
@@ -919,6 +935,31 @@ pub fn ArgsIterator(comptime Iterator: type) type {
};
}
+/// Create an args iterator for the process args. This will skip argv0.
+pub fn argsIterator(alloc_gpa: Allocator) internal_os.args.ArgIterator.InitError!ArgsIterator(internal_os.args.ArgIterator) {
+ var iter = try internal_os.args.iterator(alloc_gpa);
+ errdefer iter.deinit();
+ _ = iter.next(); // skip argv0
+ return .{ .iterator = iter };
+}
+
+test "ArgsIterator" {
+ const testing = std.testing;
+
+ const child = try std.process.ArgIteratorGeneral(.{}).init(
+ testing.allocator,
+ "--what +list-things --a=42",
+ );
+ const Iter = ArgsIterator(@TypeOf(child));
+ var iter: Iter = .{ .iterator = child };
+ defer iter.deinit();
+
+ try testing.expectEqualStrings("--what", iter.next().?);
+ try testing.expectEqualStrings("--a=42", iter.next().?);
+ try testing.expectEqual(@as(?[]const u8, null), iter.next());
+ try testing.expectEqual(@as(?[]const u8, null), iter.next());
+}
+
/// Returns an iterator (implements "next") that reads CLI args by line.
/// Each CLI arg is expected to be a single line. This is used to implement
/// configuration files.