diff options
Diffstat (limited to 'src/args.zig')
| -rw-r--r-- | src/args.zig | 90 |
1 files changed, 90 insertions, 0 deletions
diff --git a/src/args.zig b/src/args.zig new file mode 100644 index 0000000..304596c --- /dev/null +++ b/src/args.zig | |||
| @@ -0,0 +1,90 @@ | |||
| 1 | const builtin = @import("builtin"); | ||
| 2 | const std = @import("std"); | ||
| 3 | |||
| 4 | const debug = std.debug; | ||
| 5 | const heap = std.heap; | ||
| 6 | const mem = std.mem; | ||
| 7 | const os = std.os; | ||
| 8 | |||
| 9 | /// A interface for iterating over command line arguments | ||
| 10 | pub fn Iterator(comptime E: type) type { | ||
| 11 | return struct { | ||
| 12 | const Self = @This(); | ||
| 13 | const Error = E; | ||
| 14 | |||
| 15 | nextFn: fn (iter: *Self) Error!?[]const u8, | ||
| 16 | |||
| 17 | pub fn next(iter: *Self) Error!?[]const u8 { | ||
| 18 | return iter.nextFn(iter); | ||
| 19 | } | ||
| 20 | }; | ||
| 21 | } | ||
| 22 | |||
| 23 | /// An ::ArgIterator, which iterates over a slice of arguments. | ||
| 24 | /// This implementation does not allocate. | ||
| 25 | pub const SliceIterator = struct { | ||
| 26 | const Error = error{}; | ||
| 27 | |||
| 28 | args: []const []const u8, | ||
| 29 | index: usize, | ||
| 30 | iter: Iterator(Error), | ||
| 31 | |||
| 32 | pub fn init(args: []const []const u8) SliceIterator { | ||
| 33 | return SliceIterator{ | ||
| 34 | .args = args, | ||
| 35 | .index = 0, | ||
| 36 | .iter = Iterator(Error){ .nextFn = nextFn }, | ||
| 37 | }; | ||
| 38 | } | ||
| 39 | |||
| 40 | fn nextFn(iter: *Iterator(Error)) Error!?[]const u8 { | ||
| 41 | const self = @fieldParentPtr(SliceIterator, "iter", iter); | ||
| 42 | if (self.args.len <= self.index) | ||
| 43 | return null; | ||
| 44 | |||
| 45 | defer self.index += 1; | ||
| 46 | return self.args[self.index]; | ||
| 47 | } | ||
| 48 | }; | ||
| 49 | |||
| 50 | test "clap.args.SliceIterator" { | ||
| 51 | const args = [][]const u8{ "A", "BB", "CCC" }; | ||
| 52 | var slice_iter = SliceIterator.init(args); | ||
| 53 | const iter = &slice_iter.iter; | ||
| 54 | |||
| 55 | for (args) |a| { | ||
| 56 | const b = try iter.next(); | ||
| 57 | debug.assert(mem.eql(u8, a, b.?)); | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | /// An ::ArgIterator, which wraps the ArgIterator in ::std. | ||
| 62 | /// On windows, this iterator allocates. | ||
| 63 | pub const OsIterator = struct { | ||
| 64 | const Error = os.ArgIterator.NextError; | ||
| 65 | |||
| 66 | arena: heap.ArenaAllocator, | ||
| 67 | args: os.ArgIterator, | ||
| 68 | iter: Iterator(Error), | ||
| 69 | |||
| 70 | pub fn init(allocator: *mem.Allocator) OsIterator { | ||
| 71 | return OsIterator{ | ||
| 72 | .arena = heap.ArenaAllocator.init(allocator), | ||
| 73 | .args = os.args(), | ||
| 74 | .iter = Iterator(Error){ .nextFn = nextFn }, | ||
| 75 | }; | ||
| 76 | } | ||
| 77 | |||
| 78 | pub fn deinit(iter: *OsIterator) void { | ||
| 79 | iter.arena.deinit(); | ||
| 80 | } | ||
| 81 | |||
| 82 | fn nextFn(iter: *Iterator(Error)) Error!?[]const u8 { | ||
| 83 | const self = @fieldParentPtr(OsIterator, "iter", iter); | ||
| 84 | if (builtin.os == builtin.Os.windows) { | ||
| 85 | return try self.args.next(&self.arena.allocator) orelse return null; | ||
| 86 | } else { | ||
| 87 | return self.args.nextPosix(); | ||
| 88 | } | ||
| 89 | } | ||
| 90 | }; | ||