summaryrefslogtreecommitdiff
path: root/core.zig
diff options
context:
space:
mode:
Diffstat (limited to 'core.zig')
-rw-r--r--core.zig158
1 files changed, 119 insertions, 39 deletions
diff --git a/core.zig b/core.zig
index 8b953a9..33ba59c 100644
--- a/core.zig
+++ b/core.zig
@@ -18,47 +18,84 @@ const debug = std.debug;
18/// * "-abc value" 18/// * "-abc value"
19/// * "-abc=value" 19/// * "-abc=value"
20/// * "-abcvalue" 20/// * "-abcvalue"
21/// * Long ("--long-arg"): Should be used for less common parameters, or when no single character 21/// * Long ("--long-param"): Should be used for less common parameters, or when no single character
22/// can describe the paramter. 22/// can describe the paramter.
23/// * They can take a value two different ways. 23/// * They can take a value two different ways.
24/// * "--long-arg value" 24/// * "--long-param value"
25/// * "--long-arg=value" 25/// * "--long-param=value"
26/// * Value ("some-value"): Should be used as the primary parameter of the program, like a 26/// * Command ("command"): Should be used as for sub-commands and other keywords.
27/// filename or an expression to parse. 27/// * They can take a value two different ways.
28/// * "command value"
29/// * "command=value"
30/// * Value ("value"): Should be used as the primary parameter of the program, like a filename or
31/// an expression to parse.
28pub fn Param(comptime Id: type) type { 32pub fn Param(comptime Id: type) type {
29 return struct { 33 return struct {
30 const Self = this; 34 const Self = this;
31 35
32 id: Id, 36 id: Id,
37 command: ?[]const u8,
33 short: ?u8, 38 short: ?u8,
34 long: ?[]const u8, 39 long: ?[]const u8,
35 takes_value: bool, 40 takes_value: bool,
36 41
37 /// Initialize a ::Param. 42 pub fn short(id: Id, s: u8, takes_value: bool) Self {
38 /// If ::name.len == 0, then it's a value parameter: "some-command value".
39 /// If ::name.len == 1, then it's a short parameter: "some-command -s".
40 /// If ::name.len > 1, then it's a long parameter: "some-command --long".
41 pub fn init(id: Id, name: []const u8, takes_value: bool) Self {
42 return Self{ 43 return Self{
43 .id = id, 44 .id = id,
44 .short = if (name.len == 1) name[0] else null, 45 .command = null,
45 .long = if (name.len > 1) name else null, 46 .short = s,
47 .long = null,
48 .takes_value = takes_value,
49 };
50 }
51
52 pub fn long(id: Id, l: []const u8, takes_value: bool) Self {
53 return Self{
54 .id = id,
55 .command = null,
56 .short = null,
57 .long = l,
46 .takes_value = takes_value, 58 .takes_value = takes_value,
47 }; 59 };
48 } 60 }
49 61
50 pub fn both(id: Id, short: u8, long: []const u8, takes_value: bool) Self { 62 pub fn command(id: Id, c: []const u8, takes_value: bool) Self {
63 return Self{
64 .id = id,
65 .command = c,
66 .short = null,
67 .long = null,
68 .takes_value = takes_value,
69 };
70 }
71
72 pub fn value(id: Id) Self {
73 return Self{
74 .id = id,
75 .command = null,
76 .short = null,
77 .long = null,
78 .takes_value = true,
79 };
80 }
81
82 /// Initialize a ::Param.
83 /// If ::name.len == 0, then it's a value parameter: "value".
84 /// If ::name.len == 1, then it's a short parameter: "-s".
85 /// If ::name.len > 1, then it's a long parameter: "--long".
86 pub fn smart(id: Id, name: []const u8, takes_value: bool) Self {
51 return Self{ 87 return Self{
52 .id = id, 88 .id = id,
53 .short = short, 89 .command = null,
54 .long = long, 90 .short = if (name.len == 1) name[0] else null,
91 .long = if (name.len > 1) name else null,
55 .takes_value = takes_value, 92 .takes_value = takes_value,
56 }; 93 };
57 } 94 }
58 95
59 pub fn with(param: &const Self, comptime field_name: []const u8, value: var) Self { 96 pub fn with(param: &const Self, comptime field_name: []const u8, v: var) Self {
60 var res = *param; 97 var res = *param;
61 @field(res, field_name) = value; 98 @field(res, field_name) = v;
62 return res; 99 return res;
63 } 100 }
64 }; 101 };
@@ -136,7 +173,7 @@ pub const OsArgIterator = struct {
136 if (builtin.os == builtin.Os.windows) { 173 if (builtin.os == builtin.Os.windows) {
137 return try self.args.next(allocator) ?? return null; 174 return try self.args.next(allocator) ?? return null;
138 } else { 175 } else {
139 return self.args.nextPoxix(); 176 return self.args.nextPosix();
140 } 177 }
141 } 178 }
142}; 179};
@@ -180,7 +217,7 @@ pub fn Iterator(comptime Id: type) type {
180 /// Get the next ::Arg that matches a ::Param. 217 /// Get the next ::Arg that matches a ::Param.
181 pub fn next(iter: &Self) !?Arg(Id) { 218 pub fn next(iter: &Self) !?Arg(Id) {
182 const ArgInfo = struct { 219 const ArgInfo = struct {
183 const Kind = enum { Long, Short, Value }; 220 const Kind = enum { Long, Short, Command };
184 221
185 arg: []const u8, 222 arg: []const u8,
186 kind: Kind, 223 kind: Kind,
@@ -191,7 +228,7 @@ pub fn Iterator(comptime Id: type) type {
191 const full_arg = (try iter.innerNext()) ?? return null; 228 const full_arg = (try iter.innerNext()) ?? return null;
192 const arg_info = blk: { 229 const arg_info = blk: {
193 var arg = full_arg; 230 var arg = full_arg;
194 var kind = ArgInfo.Kind.Value; 231 var kind = ArgInfo.Kind.Command;
195 232
196 if (mem.startsWith(u8, arg, "--")) { 233 if (mem.startsWith(u8, arg, "--")) {
197 arg = arg[2..]; 234 arg = arg[2..];
@@ -213,12 +250,17 @@ pub fn Iterator(comptime Id: type) type {
213 250
214 for (iter.params) |*param| { 251 for (iter.params) |*param| {
215 switch (kind) { 252 switch (kind) {
253 ArgInfo.Kind.Command,
216 ArgInfo.Kind.Long => { 254 ArgInfo.Kind.Long => {
217 const long = param.long ?? continue; 255 const match = switch (kind) {
256 ArgInfo.Kind.Command => param.command ?? continue,
257 ArgInfo.Kind.Long => param.long ?? continue,
258 else => unreachable,
259 };
218 const name = if (eql_index) |i| arg[0..i] else arg; 260 const name = if (eql_index) |i| arg[0..i] else arg;
219 const maybe_value = if (eql_index) |i| arg[i + 1..] else null; 261 const maybe_value = if (eql_index) |i| arg[i + 1..] else null;
220 262
221 if (!mem.eql(u8, name, long)) 263 if (!mem.eql(u8, name, match))
222 continue; 264 continue;
223 if (!param.takes_value) { 265 if (!param.takes_value) {
224 if (maybe_value != null) 266 if (maybe_value != null)
@@ -247,12 +289,17 @@ pub fn Iterator(comptime Id: type) type {
247 .param = param, 289 .param = param,
248 }); 290 });
249 }, 291 },
250 ArgInfo.Kind.Value => { 292 }
251 if (param.long) |_| continue; 293 }
252 if (param.short) |_| continue;
253 294
254 return Arg(Id).init(param.id, arg); 295 // We do a final pass to look for value parameters matches
255 } 296 if (kind == ArgInfo.Kind.Command) {
297 for (iter.params) |*param| {
298 if (param.short) |_| continue;
299 if (param.long) |_| continue;
300 if (param.command) |_| continue;
301
302 return Arg(Id).init(param.id, arg);
256 } 303 }
257 } 304 }
258 305
@@ -327,11 +374,11 @@ fn testNoErr(params: []const Param(u8), args: []const []const u8, ids: []const u
327 } 374 }
328} 375}
329 376
330test "clap.parse: short" { 377test "clap.core: short" {
331 const params = []Param(u8) { 378 const params = []Param(u8) {
332 Param(u8).init(0, "a", false), 379 Param(u8).smart(0, "a", false),
333 Param(u8).init(1, "b", false), 380 Param(u8).smart(1, "b", false),
334 Param(u8).init(2, "c", true), 381 Param(u8).smart(2, "c", true),
335 }; 382 };
336 383
337 testNoErr(params, [][]const u8 { "-a" }, []u8{0}, []?[]const u8{null}); 384 testNoErr(params, [][]const u8 { "-a" }, []u8{0}, []?[]const u8{null});
@@ -345,11 +392,11 @@ test "clap.parse: short" {
345 testNoErr(params, [][]const u8 { "-abc100" }, []u8{0,1,2}, []?[]const u8{null,null,"100"}); 392 testNoErr(params, [][]const u8 { "-abc100" }, []u8{0,1,2}, []?[]const u8{null,null,"100"});
346} 393}
347 394
348test "clap.parse: long" { 395test "clap.core: long" {
349 const params = []Param(u8) { 396 const params = []Param(u8) {
350 Param(u8).init(0, "aa", false), 397 Param(u8).smart(0, "aa", false),
351 Param(u8).init(1, "bb", false), 398 Param(u8).smart(1, "bb", false),
352 Param(u8).init(2, "cc", true), 399 Param(u8).smart(2, "cc", true),
353 }; 400 };
354 401
355 testNoErr(params, [][]const u8 { "--aa" }, []u8{0}, []?[]const u8{null}); 402 testNoErr(params, [][]const u8 { "--aa" }, []u8{0}, []?[]const u8{null});
@@ -358,11 +405,39 @@ test "clap.parse: long" {
358 testNoErr(params, [][]const u8 { "--cc", "100" }, []u8{2}, []?[]const u8{"100"}); 405 testNoErr(params, [][]const u8 { "--cc", "100" }, []u8{2}, []?[]const u8{"100"});
359} 406}
360 407
361test "clap.parse: both" { 408test "clap.core: command" {
409 const params = []Param(u8) {
410 Param(u8).command(0, "aa", false),
411 Param(u8).command(1, "bb", false),
412 Param(u8).command(2, "cc", true),
413 };
414
415 testNoErr(params, [][]const u8 { "aa" }, []u8{0}, []?[]const u8{null});
416 testNoErr(params, [][]const u8 { "aa", "bb" }, []u8{0,1}, []?[]const u8{null,null});
417 testNoErr(params, [][]const u8 { "cc=100" }, []u8{2}, []?[]const u8{"100"});
418 testNoErr(params, [][]const u8 { "cc", "100" }, []u8{2}, []?[]const u8{"100"});
419}
420
421test "clap.core: value" {
422 const params = []Param(u8) {
423 Param(u8).value(0),
424 };
425
426 testNoErr(params, [][]const u8 { "aa" }, []u8{0}, []?[]const u8{"aa"});
427}
428
429test "clap.core: all" {
362 const params = []Param(u8) { 430 const params = []Param(u8) {
363 Param(u8).both(0, 'a', "aa", false), 431 Param(u8).short(0, 'a', false)
364 Param(u8).both(1, 'b', "bb", false), 432 .with("long", "aa"[0..])
365 Param(u8).both(2, 'c', "cc", true), 433 .with("command", "aa"[0..]),
434 Param(u8).short(1, 'b', false)
435 .with("long", "bb"[0..])
436 .with("command", "bb"[0..]),
437 Param(u8).short(2, 'c', true)
438 .with("long", "cc"[0..])
439 .with("command", "cc"[0..]),
440 Param(u8).value(3),
366 }; 441 };
367 442
368 testNoErr(params, [][]const u8 { "-a" }, []u8{0}, []?[]const u8{null}); 443 testNoErr(params, [][]const u8 { "-a" }, []u8{0}, []?[]const u8{null});
@@ -378,4 +453,9 @@ test "clap.parse: both" {
378 testNoErr(params, [][]const u8 { "--aa", "--bb" }, []u8{0,1}, []?[]const u8{null,null}); 453 testNoErr(params, [][]const u8 { "--aa", "--bb" }, []u8{0,1}, []?[]const u8{null,null});
379 testNoErr(params, [][]const u8 { "--cc=100" }, []u8{2}, []?[]const u8{"100"}); 454 testNoErr(params, [][]const u8 { "--cc=100" }, []u8{2}, []?[]const u8{"100"});
380 testNoErr(params, [][]const u8 { "--cc", "100" }, []u8{2}, []?[]const u8{"100"}); 455 testNoErr(params, [][]const u8 { "--cc", "100" }, []u8{2}, []?[]const u8{"100"});
456 testNoErr(params, [][]const u8 { "aa" }, []u8{0}, []?[]const u8{null});
457 testNoErr(params, [][]const u8 { "aa", "bb" }, []u8{0,1}, []?[]const u8{null,null});
458 testNoErr(params, [][]const u8 { "cc=100" }, []u8{2}, []?[]const u8{"100"});
459 testNoErr(params, [][]const u8 { "cc", "100" }, []u8{2}, []?[]const u8{"100"});
460 testNoErr(params, [][]const u8 { "dd" }, []u8{3}, []?[]const u8{"dd"});
381} 461}