summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jimmi Holst Christensen2018-04-27 00:16:32 +0200
committerGravatar Jimmi Holst Christensen2018-04-27 00:16:32 +0200
commit5f9278c93e181e8a47d4d9a3dd5a4f5ac2958ac0 (patch)
tree74174dd6551ec5f255aa1f72d7554f12e8cc9f03
parentFixed test (diff)
downloadzig-clap-5f9278c93e181e8a47d4d9a3dd5a4f5ac2958ac0.tar.gz
zig-clap-5f9278c93e181e8a47d4d9a3dd5a4f5ac2958ac0.tar.xz
zig-clap-5f9278c93e181e8a47d4d9a3dd5a4f5ac2958ac0.zip
Added the ability to have argument have indexs
-rw-r--r--clap.zig129
1 files changed, 81 insertions, 48 deletions
diff --git a/clap.zig b/clap.zig
index 41713a4..3cd624e 100644
--- a/clap.zig
+++ b/clap.zig
@@ -53,19 +53,31 @@ pub fn Clap(comptime Result: type) type {
53 53
54 arg: []const u8, 54 arg: []const u8,
55 kind: Kind, 55 kind: Kind,
56 after_eql: ?[]const u8,
57 }; 56 };
58 57
59 const Iterator = struct { 58 const Iterator = struct {
60 index: usize, 59 index: usize,
61 slice: []const []const u8, 60 slice: []const []const u8,
62 61
62 const Pair = struct {
63 value: []const u8,
64 index: usize,
65 };
66
63 pub fn next(it: &this) ?[]const u8 { 67 pub fn next(it: &this) ?[]const u8 {
68 const res = it.nextWithIndex() ?? return null;
69 return res.value;
70 }
71
72 pub fn nextWithIndex(it: &this) ?Pair {
64 if (it.index >= it.slice.len) 73 if (it.index >= it.slice.len)
65 return null; 74 return null;
66 75
67 defer it.index += 1; 76 defer it.index += 1;
68 return it.slice[it.index]; 77 return Pair {
78 .value = it.slice[it.index],
79 .index = it.index,
80 };
69 } 81 }
70 }; 82 };
71 83
@@ -88,9 +100,9 @@ pub fn Clap(comptime Result: type) type {
88 var result = *defaults; 100 var result = *defaults;
89 101
90 var it = Iterator { .index = 0, .slice = arguments }; 102 var it = Iterator { .index = 0, .slice = arguments };
91 while (it.next()) |item| { 103 while (it.nextWithIndex()) |item| {
92 const arg_info = blk: { 104 const arg_info = blk: {
93 var arg = item; 105 var arg = item.value;
94 var kind = Arg.Kind.Value; 106 var kind = Arg.Kind.Value;
95 107
96 if (mem.startsWith(u8, arg, "--")) { 108 if (mem.startsWith(u8, arg, "--")) {
@@ -101,71 +113,79 @@ pub fn Clap(comptime Result: type) type {
101 kind = Arg.Kind.Short; 113 kind = Arg.Kind.Short;
102 } 114 }
103 115
104 if (kind == Arg.Kind.Value) 116 break :blk Arg { .arg = arg, .kind = kind };
105 break :blk Arg { .arg = arg, .kind = kind, .after_eql = null };
106
107
108 if (mem.indexOfScalar(u8, arg, '=')) |index| {
109 break :blk Arg { .arg = arg[0..index], .kind = kind, .after_eql = arg[index + 1..] };
110 } else {
111 break :blk Arg { .arg = arg, .kind = kind, .after_eql = null };
112 }
113 }; 117 };
114 const arg = arg_info.arg; 118 const arg = arg_info.arg;
119 const arg_index = item.index;
115 const kind = arg_info.kind; 120 const kind = arg_info.kind;
116 const after_eql = arg_info.after_eql; 121 const eql_index = mem.indexOfScalar(u8, arg, '=');
117 122
118 success: { 123 success: {
124 // TODO: Revert a lot of if statements when inline loop compiler bugs have been fixed
119 switch (kind) { 125 switch (kind) {
120 // TODO: Handle subcommands 126 // TODO: Handle subcommands
121 Arg.Kind.Value => { 127 Arg.Kind.Value => {
122 var required_index = usize(0); 128 var required_index = usize(0);
123 inline for (command.arguments) |option| { 129 inline for (command.arguments) |option| {
124 defer if (option.required) required_index += 1; 130 defer if (option.required) required_index += 1;
131
125 if (option.short != null) continue; 132 if (option.short != null) continue;
126 if (option.long != null) continue; 133 if (option.long != null) continue;
134 const has_right_index = if (option.index) |index| index == it.index else true;
127 135
128 if (option.takes_value) |parser| { 136 if (has_right_index) {
129 try parser.parse(&@field(result, option.field), arg); 137 if (option.takes_value) |parser| {
130 } else { 138 try parser.parse(&@field(result, option.field), arg);
131 @field(result, option.field) = true; 139 } else {
132 } 140 @field(result, option.field) = true;
141 }
133 142
134 required = newRequired(option, required, required_index); 143 required = newRequired(option, required, required_index);
135 break :success; 144 break :success;
145 }
136 } 146 }
137 }, 147 },
138 Arg.Kind.Short => { 148 Arg.Kind.Short => {
139 const arg_len = arg.len; 149 if (arg.len == 0) return error.InvalidArg;
140 if (arg.len == 0) return error.FoundShortOptionWithNoName; 150
141 short_arg_loop: for (arg[0..arg.len - 1]) |short_arg| { 151 const end = (eql_index ?? arg.len) - 1;
152
153 short_arg_loop:
154 for (arg[0..end]) |short_arg, i| {
142 var required_index = usize(0); 155 var required_index = usize(0);
156
143 inline for (command.arguments) |option| { 157 inline for (command.arguments) |option| {
144 defer if (option.required) required_index += 1; 158 defer if (option.required) required_index += 1;
159
145 const short = option.short ?? continue; 160 const short = option.short ?? continue;
146 if (short_arg == short) { 161 const has_right_index = if (option.index) |index| index == arg_index else true;
147 if (option.takes_value) |_| return error.OptionMissingValue;
148 162
149 @field(result, option.field) = true; 163 if (has_right_index) {
150 required = newRequired(option, required, required_index); 164 if (short_arg == short) {
151 continue :short_arg_loop; 165 if (option.takes_value) |_| return error.OptionMissingValue;
166
167 @field(result, option.field) = true;
168 required = newRequired(option, required, required_index);
169 continue :short_arg_loop;
170 }
152 } 171 }
153 } 172 }
154
155 return error.InvalidArgument;
156 } 173 }
157 174
158 const last_arg = arg[arg.len - 1]; 175 const last_arg = arg[end];
159 var required_index = usize(0); 176 var required_index = usize(0);
160 inline for (command.arguments) |option| { 177 inline for (command.arguments) |option| {
161 defer if (option.required) required_index += 1; 178 defer if (option.required) required_index += 1;
179
162 const short = option.short ?? continue; 180 const short = option.short ?? continue;
181 const has_right_index = if (option.index) |index| index == arg_index else true;
163 182
164 if (last_arg == short) { 183 if (has_right_index and last_arg == short) {
165 if (option.takes_value) |parser| { 184 if (option.takes_value) |parser| {
166 const value = after_eql ?? it.next() ?? return error.OptionMissingValue; 185 const value = if (eql_index) |index| arg[index + 1..] else it.next() ?? return error.ArgMissingValue;
167 try parser.parse(&@field(result, option.field), value); 186 try parser.parse(&@field(result, option.field), value);
168 } else { 187 } else {
188 if (eql_index) |_| return error.ArgTakesNoValue;
169 @field(result, option.field) = true; 189 @field(result, option.field) = true;
170 } 190 }
171 191
@@ -178,11 +198,13 @@ pub fn Clap(comptime Result: type) type {
178 var required_index = usize(0); 198 var required_index = usize(0);
179 inline for (command.arguments) |option| { 199 inline for (command.arguments) |option| {
180 defer if (option.required) required_index += 1; 200 defer if (option.required) required_index += 1;
201
181 const long = option.long ?? continue; 202 const long = option.long ?? continue;
203 const has_right_index = if (option.index) |index| index == arg_index else true;
182 204
183 if (mem.eql(u8, arg, long)) { 205 if (has_right_index and mem.eql(u8, arg, long)) {
184 if (option.takes_value) |parser| { 206 if (option.takes_value) |parser| {
185 const value = after_eql ?? it.next() ?? return error.OptionMissingValue; 207 const value = if (eql_index) |index| arg[index + 1..] else it.next() ?? return error.ArgMissingValue;
186 try parser.parse(&@field(result, option.field), value); 208 try parser.parse(&@field(result, option.field), value);
187 } else { 209 } else {
188 @field(result, option.field) = true; 210 @field(result, option.field) = true;
@@ -195,12 +217,12 @@ pub fn Clap(comptime Result: type) type {
195 } 217 }
196 } 218 }
197 219
198 return error.InvalidArgument; 220 return error.InvalidArg;
199 } 221 }
200 } 222 }
201 223
202 if (required != 0) { 224 if (required != 0) {
203 return error.RequiredArgumentWasntHandled; 225 return error.RequiredArgNotHandled;
204 } 226 }
205 227
206 return result; 228 return result;
@@ -271,6 +293,7 @@ pub const Argument = struct {
271 required: bool, 293 required: bool,
272 short: ?u8, 294 short: ?u8,
273 long: ?[]const u8, 295 long: ?[]const u8,
296 index: ?usize,
274 297
275 pub fn field(field_name: []const u8) Argument { 298 pub fn field(field_name: []const u8) Argument {
276 return Argument { 299 return Argument {
@@ -280,18 +303,14 @@ pub const Argument = struct {
280 .required = false, 303 .required = false,
281 .short = null, 304 .short = null,
282 .long = null, 305 .long = null,
306 .index = null,
283 }; 307 };
284 } 308 }
285 309
286 pub fn arg(s: []const u8) Argument { 310 pub fn arg(s: []const u8) Argument {
287 return Argument { 311 return Argument.field(s)
288 .field = s, 312 .with("short", if (s.len == 1) s[0] else null)
289 .help = "", 313 .with("long", if (s.len != 1) s else null);
290 .takes_value = null,
291 .required = false,
292 .short = if (s.len == 1) s[0] else null,
293 .long = if (s.len != 1) s else null,
294 };
295 } 314 }
296 315
297 pub fn with(argument: &const Argument, comptime field_name: []const u8, value: var) Argument { 316 pub fn with(argument: &const Argument, comptime field_name: []const u8, value: var) Argument {
@@ -377,8 +396,8 @@ fn testNoErr(comptime clap: &const Clap(Options), args: []const []const u8, expe
377 assert(expected.cc == actual.cc); 396 assert(expected.cc == actual.cc);
378} 397}
379 398
380fn testErr(args: []const []const u8, expected: error) void { 399fn testErr(comptime clap: &const Clap(Options), args: []const []const u8, expected: error) void {
381 if (clap.parse(case.args)) |actual| { 400 if (clap.parse(args)) |actual| {
382 unreachable; 401 unreachable;
383 } else |err| { 402 } else |err| {
384 assert(err == expected); 403 assert(err == expected);
@@ -458,3 +477,17 @@ test "clap.parse: value int" {
458 477
459 testNoErr(clap, [][]const u8 { "100" }, default.with("int", 100)); 478 testNoErr(clap, [][]const u8 { "100" }, default.with("int", 100));
460} 479}
480
481test "clap.parse: index" {
482 const clap = comptime Clap(Options).init(default).with("command",
483 Command.init("").with("arguments",
484 []Argument {
485 Argument.arg("a").with("index", 0),
486 Argument.arg("b").with("index", 1),
487 }
488 )
489 );
490
491 testNoErr(clap, [][]const u8 { "-a", "-b" }, default.with("a", true).with("b", true));
492 testErr(clap, [][]const u8 { "-b", "-a" }, error.InvalidArg);
493}