summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jimmi Holst Christensen2019-01-17 15:52:45 +0100
committerGravatar Jimmi Holst Christensen2019-01-17 15:52:45 +0100
commit938b3ff7787a3e99bc41cac9add5ff25835eee22 (patch)
treeefefe7cd2d0cbaebdb664de37848a5ad01a87fef
parentRenamed helpEx to helpFull and added a new helpEx that wraps helpFull (diff)
downloadzig-clap-938b3ff7787a3e99bc41cac9add5ff25835eee22.tar.gz
zig-clap-938b3ff7787a3e99bc41cac9add5ff25835eee22.tar.xz
zig-clap-938b3ff7787a3e99bc41cac9add5ff25835eee22.zip
Refactored the arg iterators to new be static interface implementations
* This makes arg iterators easier to understand and implement * It should also be faster than using the fieldToParent builtin
-rw-r--r--example/comptime-clap.zig7
-rw-r--r--example/streaming-clap.zig7
-rw-r--r--src/args.zig46
-rw-r--r--src/comptime.zig8
-rw-r--r--src/streaming.zig10
5 files changed, 32 insertions, 46 deletions
diff --git a/example/comptime-clap.zig b/example/comptime-clap.zig
index b275dc7..8517db8 100644
--- a/example/comptime-clap.zig
+++ b/example/comptime-clap.zig
@@ -27,15 +27,14 @@ pub fn main() !void {
27 27
28 // We then initialize an argument iterator. We will use the OsIterator as it nicely 28 // We then initialize an argument iterator. We will use the OsIterator as it nicely
29 // wraps iterating over arguments the most efficient way on each os. 29 // wraps iterating over arguments the most efficient way on each os.
30 var os_iter = clap.args.OsIterator.init(allocator); 30 var iter = clap.args.OsIterator.init(allocator);
31 const iter = &os_iter.iter; 31 defer iter.deinit();
32 defer os_iter.deinit();
33 32
34 // Consume the exe arg. 33 // Consume the exe arg.
35 const exe = try iter.next(); 34 const exe = try iter.next();
36 35
37 // Finally we can parse the arguments 36 // Finally we can parse the arguments
38 var args = try clap.ComptimeClap([]const u8, params).parse(allocator, clap.args.OsIterator.Error, iter); 37 var args = try clap.ComptimeClap([]const u8, params).parse(allocator, clap.args.OsIterator, &iter);
39 defer args.deinit(); 38 defer args.deinit();
40 39
41 // clap.help is a function that can print a simple help message, given a 40 // clap.help is a function that can print a simple help message, given a
diff --git a/example/streaming-clap.zig b/example/streaming-clap.zig
index 57ebe71..a52a222 100644
--- a/example/streaming-clap.zig
+++ b/example/streaming-clap.zig
@@ -17,15 +17,14 @@ pub fn main() !void {
17 17
18 // We then initialize an argument iterator. We will use the OsIterator as it nicely 18 // We then initialize an argument iterator. We will use the OsIterator as it nicely
19 // wraps iterating over arguments the most efficient way on each os. 19 // wraps iterating over arguments the most efficient way on each os.
20 var os_iter = clap.args.OsIterator.init(allocator); 20 var iter = clap.args.OsIterator.init(allocator);
21 const iter = &os_iter.iter; 21 defer iter.deinit();
22 defer os_iter.deinit();
23 22
24 // Consume the exe arg. 23 // Consume the exe arg.
25 const exe = try iter.next(); 24 const exe = try iter.next();
26 25
27 // Finally we initialize our streaming parser. 26 // Finally we initialize our streaming parser.
28 var parser = clap.StreamingClap(u8, clap.args.OsIterator.Error).init(params, iter); 27 var parser = clap.StreamingClap(u8, clap.args.OsIterator).init(params, &iter);
29 28
30 // Because we use a streaming parser, we have to consume each argument parsed individually. 29 // Because we use a streaming parser, we have to consume each argument parsed individually.
31 while (try parser.next()) |arg| { 30 while (try parser.next()) |arg| {
diff --git a/src/args.zig b/src/args.zig
index 304596c..8706ebc 100644
--- a/src/args.zig
+++ b/src/args.zig
@@ -6,51 +6,42 @@ const heap = std.heap;
6const mem = std.mem; 6const mem = std.mem;
7const os = std.os; 7const os = std.os;
8 8
9/// A interface for iterating over command line arguments 9/// An example of what methods should be implemented on an arg iterator.
10pub fn Iterator(comptime E: type) type { 10pub const ExampleArgIterator = struct {
11 return struct { 11 const Error = error{};
12 const Self = @This();
13 const Error = E;
14
15 nextFn: fn (iter: *Self) Error!?[]const u8,
16 12
17 pub fn next(iter: *Self) Error!?[]const u8 { 13 pub fn next(iter: *ExampleArgIterator) Error!?[]const u8 {
18 return iter.nextFn(iter); 14 return "2";
19 } 15 }
20 }; 16};
21}
22 17
23/// An ::ArgIterator, which iterates over a slice of arguments. 18/// An argument iterator which iterates over a slice of arguments.
24/// This implementation does not allocate. 19/// This implementation does not allocate.
25pub const SliceIterator = struct { 20pub const SliceIterator = struct {
26 const Error = error{}; 21 const Error = error{};
27 22
28 args: []const []const u8, 23 args: []const []const u8,
29 index: usize, 24 index: usize,
30 iter: Iterator(Error),
31 25
32 pub fn init(args: []const []const u8) SliceIterator { 26 pub fn init(args: []const []const u8) SliceIterator {
33 return SliceIterator{ 27 return SliceIterator{
34 .args = args, 28 .args = args,
35 .index = 0, 29 .index = 0,
36 .iter = Iterator(Error){ .nextFn = nextFn },
37 }; 30 };
38 } 31 }
39 32
40 fn nextFn(iter: *Iterator(Error)) Error!?[]const u8 { 33 pub fn next(iter: *SliceIterator) Error!?[]const u8 {
41 const self = @fieldParentPtr(SliceIterator, "iter", iter); 34 if (iter.args.len <= iter.index)
42 if (self.args.len <= self.index)
43 return null; 35 return null;
44 36
45 defer self.index += 1; 37 defer iter.index += 1;
46 return self.args[self.index]; 38 return iter.args[iter.index];
47 } 39 }
48}; 40};
49 41
50test "clap.args.SliceIterator" { 42test "clap.args.SliceIterator" {
51 const args = [][]const u8{ "A", "BB", "CCC" }; 43 const args = [][]const u8{ "A", "BB", "CCC" };
52 var slice_iter = SliceIterator.init(args); 44 var iter = SliceIterator.init(args);
53 const iter = &slice_iter.iter;
54 45
55 for (args) |a| { 46 for (args) |a| {
56 const b = try iter.next(); 47 const b = try iter.next();
@@ -58,20 +49,18 @@ test "clap.args.SliceIterator" {
58 } 49 }
59} 50}
60 51
61/// An ::ArgIterator, which wraps the ArgIterator in ::std. 52/// An argument iterator which wraps the ArgIterator in ::std.
62/// On windows, this iterator allocates. 53/// On windows, this iterator allocates.
63pub const OsIterator = struct { 54pub const OsIterator = struct {
64 const Error = os.ArgIterator.NextError; 55 const Error = os.ArgIterator.NextError;
65 56
66 arena: heap.ArenaAllocator, 57 arena: heap.ArenaAllocator,
67 args: os.ArgIterator, 58 args: os.ArgIterator,
68 iter: Iterator(Error),
69 59
70 pub fn init(allocator: *mem.Allocator) OsIterator { 60 pub fn init(allocator: *mem.Allocator) OsIterator {
71 return OsIterator{ 61 return OsIterator{
72 .arena = heap.ArenaAllocator.init(allocator), 62 .arena = heap.ArenaAllocator.init(allocator),
73 .args = os.args(), 63 .args = os.args(),
74 .iter = Iterator(Error){ .nextFn = nextFn },
75 }; 64 };
76 } 65 }
77 66
@@ -79,12 +68,11 @@ pub const OsIterator = struct {
79 iter.arena.deinit(); 68 iter.arena.deinit();
80 } 69 }
81 70
82 fn nextFn(iter: *Iterator(Error)) Error!?[]const u8 { 71 pub fn next(iter: *OsIterator) Error!?[]const u8 {
83 const self = @fieldParentPtr(OsIterator, "iter", iter);
84 if (builtin.os == builtin.Os.windows) { 72 if (builtin.os == builtin.Os.windows) {
85 return try self.args.next(&self.arena.allocator) orelse return null; 73 return try iter.args.next(&iter.arena.allocator) orelse return null;
86 } else { 74 } else {
87 return self.args.nextPosix(); 75 return iter.args.nextPosix();
88 } 76 }
89 } 77 }
90}; 78};
diff --git a/src/comptime.zig b/src/comptime.zig
index 808915d..0a4257e 100644
--- a/src/comptime.zig
+++ b/src/comptime.zig
@@ -34,7 +34,7 @@ pub fn ComptimeClap(comptime Id: type, comptime params: []const clap.Param(Id))
34 pos: []const []const u8, 34 pos: []const []const u8,
35 allocator: *mem.Allocator, 35 allocator: *mem.Allocator,
36 36
37 pub fn parse(allocator: *mem.Allocator, comptime ArgError: type, iter: *clap.args.Iterator(ArgError)) !@This() { 37 pub fn parse(allocator: *mem.Allocator, comptime ArgIter: type, iter: *ArgIter) !@This() {
38 var pos = std.ArrayList([]const u8).init(allocator); 38 var pos = std.ArrayList([]const u8).init(allocator);
39 var res = @This(){ 39 var res = @This(){
40 .options = []?[]const u8{null} ** options, 40 .options = []?[]const u8{null} ** options,
@@ -43,7 +43,7 @@ pub fn ComptimeClap(comptime Id: type, comptime params: []const clap.Param(Id))
43 .allocator = allocator, 43 .allocator = allocator,
44 }; 44 };
45 45
46 var stream = clap.StreamingClap(usize, ArgError).init(converted_params, iter); 46 var stream = clap.StreamingClap(usize, ArgIter).init(converted_params, iter);
47 while (try stream.next()) |arg| { 47 while (try stream.next()) |arg| {
48 const param = arg.param; 48 const param = arg.param;
49 if (param.names.long == null and param.names.short == null) { 49 if (param.names.long == null and param.names.short == null) {
@@ -124,10 +124,10 @@ test "clap.comptime.ComptimeClap" {
124 124
125 var buf: [1024]u8 = undefined; 125 var buf: [1024]u8 = undefined;
126 var fb_allocator = heap.FixedBufferAllocator.init(buf[0..]); 126 var fb_allocator = heap.FixedBufferAllocator.init(buf[0..]);
127 var arg_iter = clap.args.SliceIterator.init([][]const u8{ 127 var iter = clap.args.SliceIterator.init([][]const u8{
128 "-a", "-c", "0", "something", 128 "-a", "-c", "0", "something",
129 }); 129 });
130 var args = try Clap.parse(&fb_allocator.allocator, clap.args.SliceIterator.Error, &arg_iter.iter); 130 var args = try Clap.parse(&fb_allocator.allocator, clap.args.SliceIterator, &iter);
131 defer args.deinit(); 131 defer args.deinit();
132 132
133 debug.assert(args.flag("-a")); 133 debug.assert(args.flag("-a"));
diff --git a/src/streaming.zig b/src/streaming.zig
index f33fd96..babc573 100644
--- a/src/streaming.zig
+++ b/src/streaming.zig
@@ -28,7 +28,7 @@ pub fn Arg(comptime Id: type) type {
28/// A command line argument parser which, given an ::ArgIterator, will parse arguments according 28/// A command line argument parser which, given an ::ArgIterator, will parse arguments according
29/// to the ::params. ::StreamingClap parses in an iterating manner, so you have to use a loop together with 29/// to the ::params. ::StreamingClap parses in an iterating manner, so you have to use a loop together with
30/// ::StreamingClap.next to parse all the arguments of your program. 30/// ::StreamingClap.next to parse all the arguments of your program.
31pub fn StreamingClap(comptime Id: type, comptime ArgError: type) type { 31pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type {
32 return struct { 32 return struct {
33 const State = union(enum) { 33 const State = union(enum) {
34 Normal, 34 Normal,
@@ -41,10 +41,10 @@ pub fn StreamingClap(comptime Id: type, comptime ArgError: type) type {
41 }; 41 };
42 42
43 params: []const clap.Param(Id), 43 params: []const clap.Param(Id),
44 iter: *args.Iterator(ArgError), 44 iter: *ArgIterator,
45 state: State, 45 state: State,
46 46
47 pub fn init(params: []const clap.Param(Id), iter: *args.Iterator(ArgError)) @This() { 47 pub fn init(params: []const clap.Param(Id), iter: *ArgIterator) @This() {
48 var res = @This(){ 48 var res = @This(){
49 .params = params, 49 .params = params,
50 .iter = iter, 50 .iter = iter,
@@ -189,8 +189,8 @@ pub fn StreamingClap(comptime Id: type, comptime ArgError: type) type {
189} 189}
190 190
191fn testNoErr(params: []const clap.Param(u8), args_strings: []const []const u8, results: []const Arg(u8)) void { 191fn testNoErr(params: []const clap.Param(u8), args_strings: []const []const u8, results: []const Arg(u8)) void {
192 var arg_iter = args.SliceIterator.init(args_strings); 192 var iter = args.SliceIterator.init(args_strings);
193 var c = StreamingClap(u8, args.SliceIterator.Error).init(params, &arg_iter.iter); 193 var c = StreamingClap(u8, args.SliceIterator).init(params, &iter);
194 194
195 for (results) |res| { 195 for (results) |res| {
196 const arg = (c.next() catch unreachable) orelse unreachable; 196 const arg = (c.next() catch unreachable) orelse unreachable;