summaryrefslogtreecommitdiff
path: root/clap
diff options
context:
space:
mode:
Diffstat (limited to 'clap')
-rw-r--r--clap/comptime.zig14
-rw-r--r--clap/streaming.zig84
2 files changed, 54 insertions, 44 deletions
diff --git a/clap/comptime.zig b/clap/comptime.zig
index b0edb2a..e8aae30 100644
--- a/clap/comptime.zig
+++ b/clap/comptime.zig
@@ -6,7 +6,11 @@ const heap = std.heap;
6const mem = std.mem; 6const mem = std.mem;
7const debug = std.debug; 7const debug = std.debug;
8 8
9pub fn ComptimeClap(comptime Id: type, comptime params: []const clap.Param(Id)) type { 9pub fn ComptimeClap(
10 comptime Id: type,
11 comptime ArgIter: type,
12 comptime params: []const clap.Param(Id),
13) type {
10 var flags: usize = 0; 14 var flags: usize = 0;
11 var single_options: usize = 0; 15 var single_options: usize = 0;
12 var multi_options: usize = 0; 16 var multi_options: usize = 0;
@@ -38,7 +42,7 @@ pub fn ComptimeClap(comptime Id: type, comptime params: []const clap.Param(Id))
38 pos: []const []const u8, 42 pos: []const []const u8,
39 allocator: *mem.Allocator, 43 allocator: *mem.Allocator,
40 44
41 pub fn parse(allocator: *mem.Allocator, comptime ArgIter: type, iter: *ArgIter) !@This() { 45 pub fn parse(allocator: *mem.Allocator, iter: *ArgIter, diag: ?*clap.Diagnostic) !@This() {
42 var multis = [_]std.ArrayList([]const u8){undefined} ** multi_options; 46 var multis = [_]std.ArrayList([]const u8){undefined} ** multi_options;
43 for (multis) |*multi| { 47 for (multis) |*multi| {
44 multi.* = std.ArrayList([]const u8).init(allocator); 48 multi.* = std.ArrayList([]const u8).init(allocator);
@@ -58,7 +62,7 @@ pub fn ComptimeClap(comptime Id: type, comptime params: []const clap.Param(Id))
58 .params = converted_params, 62 .params = converted_params,
59 .iter = iter, 63 .iter = iter,
60 }; 64 };
61 while (try stream.next()) |arg| { 65 while (try stream.next(diag)) |arg| {
62 const param = arg.param; 66 const param = arg.param;
63 if (param.names.long == null and param.names.short == null) { 67 if (param.names.long == null and param.names.short == null) {
64 try pos.append(arg.value.?); 68 try pos.append(arg.value.?);
@@ -143,7 +147,7 @@ pub fn ComptimeClap(comptime Id: type, comptime params: []const clap.Param(Id))
143} 147}
144 148
145test "clap.comptime.ComptimeClap" { 149test "clap.comptime.ComptimeClap" {
146 const Clap = ComptimeClap(clap.Help, comptime &[_]clap.Param(clap.Help){ 150 const Clap = ComptimeClap(clap.Help, clap.args.SliceIterator, comptime &[_]clap.Param(clap.Help){
147 clap.parseParam("-a, --aa ") catch unreachable, 151 clap.parseParam("-a, --aa ") catch unreachable,
148 clap.parseParam("-b, --bb ") catch unreachable, 152 clap.parseParam("-b, --bb ") catch unreachable,
149 clap.parseParam("-c, --cc <V>") catch unreachable, 153 clap.parseParam("-c, --cc <V>") catch unreachable,
@@ -160,7 +164,7 @@ test "clap.comptime.ComptimeClap" {
160 "-a", "-c", "0", "something", "-d", "a", "--dd", "b", 164 "-a", "-c", "0", "something", "-d", "a", "--dd", "b",
161 }, 165 },
162 }; 166 };
163 var args = try Clap.parse(&fb_allocator.allocator, clap.args.SliceIterator, &iter); 167 var args = try Clap.parse(&fb_allocator.allocator, &iter, null);
164 defer args.deinit(); 168 defer args.deinit();
165 169
166 testing.expect(args.flag("-a")); 170 testing.expect(args.flag("-a"));
diff --git a/clap/streaming.zig b/clap/streaming.zig
index b843bff..90c4e02 100644
--- a/clap/streaming.zig
+++ b/clap/streaming.zig
@@ -24,8 +24,8 @@ pub fn Arg(comptime Id: type) type {
24pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type { 24pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type {
25 return struct { 25 return struct {
26 const State = union(enum) { 26 const State = union(enum) {
27 Normal, 27 normal,
28 Chaining: Chaining, 28 chaining: Chaining,
29 29
30 const Chaining = struct { 30 const Chaining = struct {
31 arg: []const u8, 31 arg: []const u8,
@@ -35,49 +35,48 @@ pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type {
35 35
36 params: []const clap.Param(Id), 36 params: []const clap.Param(Id),
37 iter: *ArgIterator, 37 iter: *ArgIterator,
38 state: State = State.Normal, 38 state: State = .normal,
39 39
40 /// Get the next ::Arg that matches a ::Param. 40 /// Get the next ::Arg that matches a ::Param.
41 pub fn next(parser: *@This()) !?Arg(Id) { 41 pub fn next(parser: *@This(), diag: ?*clap.Diagnostic) !?Arg(Id) {
42 const ArgInfo = struct { 42 const ArgInfo = struct {
43 const Kind = enum {
44 Long,
45 Short,
46 Positional,
47 };
48
49 arg: []const u8, 43 arg: []const u8,
50 kind: Kind, 44 kind: enum {
45 long,
46 short,
47 positional,
48 },
51 }; 49 };
52 50
53 switch (parser.state) { 51 switch (parser.state) {
54 .Normal => { 52 .normal => {
55 const full_arg = (try parser.iter.next()) orelse return null; 53 const full_arg = (try parser.iter.next()) orelse return null;
56 const arg_info = if (mem.eql(u8, full_arg, "--") or mem.eql(u8, full_arg, "-")) 54 const arg_info = if (mem.eql(u8, full_arg, "--") or mem.eql(u8, full_arg, "-"))
57 ArgInfo{ .arg = full_arg, .kind = .Positional } 55 ArgInfo{ .arg = full_arg, .kind = .positional }
58 else if (mem.startsWith(u8, full_arg, "--")) 56 else if (mem.startsWith(u8, full_arg, "--"))
59 ArgInfo{ .arg = full_arg[2..], .kind = .Long } 57 ArgInfo{ .arg = full_arg[2..], .kind = .long }
60 else if (mem.startsWith(u8, full_arg, "-")) 58 else if (mem.startsWith(u8, full_arg, "-"))
61 ArgInfo{ .arg = full_arg[1..], .kind = .Short } 59 ArgInfo{ .arg = full_arg[1..], .kind = .short }
62 else 60 else
63 ArgInfo{ .arg = full_arg, .kind = .Positional }; 61 ArgInfo{ .arg = full_arg, .kind = .positional };
64 62
65 const arg = arg_info.arg; 63 const arg = arg_info.arg;
66 const kind = arg_info.kind; 64 const kind = arg_info.kind;
67 const eql_index = mem.indexOfScalar(u8, arg, '=');
68 65
69 switch (kind) { 66 switch (kind) {
70 ArgInfo.Kind.Long => { 67 .long => {
68 const eql_index = mem.indexOfScalar(u8, arg, '=');
69 const name = if (eql_index) |i| arg[0..i] else arg;
70 const maybe_value = if (eql_index) |i| arg[i + 1 ..] else null;
71
71 for (parser.params) |*param| { 72 for (parser.params) |*param| {
72 const match = param.names.long orelse continue; 73 const match = param.names.long orelse continue;
73 const name = if (eql_index) |i| arg[0..i] else arg;
74 const maybe_value = if (eql_index) |i| arg[i + 1 ..] else null;
75 74
76 if (!mem.eql(u8, name, match)) 75 if (!mem.eql(u8, name, match))
77 continue; 76 continue;
78 if (param.takes_value == .None) { 77 if (param.takes_value == .None) {
79 if (maybe_value != null) 78 if (maybe_value != null)
80 return error.DoesntTakeValue; 79 return err(diag, param.names, error.DoesntTakeValue);
81 80
82 return Arg(Id){ .param = param }; 81 return Arg(Id){ .param = param };
83 } 82 }
@@ -86,19 +85,18 @@ pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type {
86 if (maybe_value) |v| 85 if (maybe_value) |v|
87 break :blk v; 86 break :blk v;
88 87
89 break :blk (try parser.iter.next()) orelse return error.MissingValue; 88 break :blk (try parser.iter.next()) orelse
89 return err(diag, param.names, error.MissingValue);
90 }; 90 };
91 91
92 return Arg(Id){ .param = param, .value = value }; 92 return Arg(Id){ .param = param, .value = value };
93 } 93 }
94 }, 94 },
95 ArgInfo.Kind.Short => { 95 .short => return try parser.chainging(.{
96 return try parser.chainging(State.Chaining{ 96 .arg = full_arg,
97 .arg = full_arg, 97 .index = full_arg.len - arg.len,
98 .index = (full_arg.len - arg.len), 98 }, diag),
99 }); 99 .positional => {
100 },
101 ArgInfo.Kind.Positional => {
102 for (parser.params) |*param| { 100 for (parser.params) |*param| {
103 if (param.names.long) |_| 101 if (param.names.long) |_|
104 continue; 102 continue;
@@ -110,13 +108,13 @@ pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type {
110 }, 108 },
111 } 109 }
112 110
113 return error.InvalidArgument; 111 return err(diag, .{ .long = arg }, error.InvalidArgument);
114 }, 112 },
115 .Chaining => |state| return try parser.chainging(state), 113 .chaining => |state| return try parser.chainging(state, diag),
116 } 114 }
117 } 115 }
118 116
119 fn chainging(parser: *@This(), state: State.Chaining) !?Arg(Id) { 117 fn chainging(parser: *@This(), state: State.Chaining, diag: ?*clap.Diagnostic) !?Arg(Id) {
120 const arg = state.arg; 118 const arg = state.arg;
121 const index = state.index; 119 const index = state.index;
122 const next_index = index + 1; 120 const next_index = index + 1;
@@ -129,10 +127,10 @@ pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type {
129 // Before we return, we have to set the new state of the clap 127 // Before we return, we have to set the new state of the clap
130 defer { 128 defer {
131 if (arg.len <= next_index or param.takes_value != .None) { 129 if (arg.len <= next_index or param.takes_value != .None) {
132 parser.state = State.Normal; 130 parser.state = .normal;
133 } else { 131 } else {
134 parser.state = State{ 132 parser.state = .{
135 .Chaining = State.Chaining{ 133 .chaining = .{
136 .arg = arg, 134 .arg = arg,
137 .index = next_index, 135 .index = next_index,
138 }, 136 },
@@ -144,7 +142,9 @@ pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type {
144 return Arg(Id){ .param = param }; 142 return Arg(Id){ .param = param };
145 143
146 if (arg.len <= next_index) { 144 if (arg.len <= next_index) {
147 const value = (try parser.iter.next()) orelse return error.MissingValue; 145 const value = (try parser.iter.next()) orelse
146 return err(diag, param.names, error.MissingValue);
147
148 return Arg(Id){ .param = param, .value = value }; 148 return Arg(Id){ .param = param, .value = value };
149 } 149 }
150 150
@@ -154,7 +154,13 @@ pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type {
154 return Arg(Id){ .param = param, .value = arg[next_index..] }; 154 return Arg(Id){ .param = param, .value = arg[next_index..] };
155 } 155 }
156 156
157 return error.InvalidArgument; 157 return err(diag, .{ .short = arg[index] }, error.InvalidArgument);
158 }
159
160 fn err(diag: ?*clap.Diagnostic, names: clap.Names, _err: var) @TypeOf(_err) {
161 if (diag) |d|
162 d.name = names;
163 return _err;
158 } 164 }
159 }; 165 };
160} 166}
@@ -167,7 +173,7 @@ fn testNoErr(params: []const clap.Param(u8), args_strings: []const []const u8, r
167 }; 173 };
168 174
169 for (results) |res| { 175 for (results) |res| {
170 const arg = (c.next() catch unreachable) orelse unreachable; 176 const arg = (c.next(null) catch unreachable) orelse unreachable;
171 testing.expectEqual(res.param, arg.param); 177 testing.expectEqual(res.param, arg.param);
172 const expected_value = res.value orelse { 178 const expected_value = res.value orelse {
173 testing.expectEqual(@as(@TypeOf(arg.value), null), arg.value); 179 testing.expectEqual(@as(@TypeOf(arg.value), null), arg.value);
@@ -177,7 +183,7 @@ fn testNoErr(params: []const clap.Param(u8), args_strings: []const []const u8, r
177 testing.expectEqualSlices(u8, expected_value, actual_value); 183 testing.expectEqualSlices(u8, expected_value, actual_value);
178 } 184 }
179 185
180 if (c.next() catch unreachable) |_| 186 if (c.next(null) catch unreachable) |_|
181 unreachable; 187 unreachable;
182} 188}
183 189