summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Jimmi HC2018-05-31 16:00:44 +0200
committerGravatar Jimmi HC2018-05-31 16:00:44 +0200
commitf4e53cd6149ed4dcbfd8b81a6427b1b652d0a472 (patch)
tree7ca591977efac33ebc9aa51aab7356a4faa97d46 /src
parentReworked the core. (diff)
downloadzig-clap-f4e53cd6149ed4dcbfd8b81a6427b1b652d0a472.tar.gz
zig-clap-f4e53cd6149ed4dcbfd8b81a6427b1b652d0a472.tar.xz
zig-clap-f4e53cd6149ed4dcbfd8b81a6427b1b652d0a472.zip
Started work on the proper structure for the lib
Diffstat (limited to '')
-rw-r--r--src/core.zig (renamed from core.zig)121
-rw-r--r--src/extended.zig264
2 files changed, 264 insertions, 121 deletions
diff --git a/core.zig b/src/core.zig
index 306ff63..a3fb44c 100644
--- a/core.zig
+++ b/src/core.zig
@@ -370,124 +370,3 @@ pub fn Clap(comptime Id: type) type {
370 } 370 }
371 }; 371 };
372} 372}
373
374fn testNoErr(params: []const Param(u8), args: []const []const u8, ids: []const u8, values: []const ?[]const u8) void {
375 var arg_iter = ArgSliceIterator.init(args);
376 var iter = Clap(u8).init(params, &arg_iter.iter, debug.global_allocator);
377
378 var i: usize = 0;
379 while (iter.next() catch unreachable) |arg| : (i += 1) {
380 debug.assert(ids[i] == arg.id);
381 const expected_value = values[i] ?? {
382 debug.assert(arg.value == null);
383 continue;
384 };
385 const actual_value = arg.value ?? unreachable;
386
387 debug.assert(mem.eql(u8, expected_value, actual_value));
388 }
389}
390
391test "clap.core: short" {
392 const params = []Param(u8) {
393 Param(u8).init(0, false, Names.short('a')),
394 Param(u8).init(1, false, Names.short('b')),
395 Param(u8).init(2, true, Names.short('c')),
396 };
397
398 testNoErr(params, [][]const u8 { "-a" }, []u8{0}, []?[]const u8{null});
399 testNoErr(params, [][]const u8 { "-a", "-b" }, []u8{0,1}, []?[]const u8{null,null});
400 testNoErr(params, [][]const u8 { "-ab" }, []u8{0,1}, []?[]const u8{null,null});
401 testNoErr(params, [][]const u8 { "-c=100" }, []u8{2}, []?[]const u8{"100"});
402 testNoErr(params, [][]const u8 { "-c100" }, []u8{2}, []?[]const u8{"100"});
403 testNoErr(params, [][]const u8 { "-c", "100" }, []u8{2}, []?[]const u8{"100"});
404 testNoErr(params, [][]const u8 { "-abc", "100" }, []u8{0,1,2}, []?[]const u8{null,null,"100"});
405 testNoErr(params, [][]const u8 { "-abc=100" }, []u8{0,1,2}, []?[]const u8{null,null,"100"});
406 testNoErr(params, [][]const u8 { "-abc100" }, []u8{0,1,2}, []?[]const u8{null,null,"100"});
407}
408
409test "clap.core: long" {
410 const params = []Param(u8) {
411 Param(u8).init(0, false, Names.long("aa")),
412 Param(u8).init(1, false, Names.long("bb")),
413 Param(u8).init(2, true, Names.long("cc")),
414 };
415
416 testNoErr(params, [][]const u8 { "--aa" }, []u8{0}, []?[]const u8{null});
417 testNoErr(params, [][]const u8 { "--aa", "--bb" }, []u8{0,1}, []?[]const u8{null,null});
418 testNoErr(params, [][]const u8 { "--cc=100" }, []u8{2}, []?[]const u8{"100"});
419 testNoErr(params, [][]const u8 { "--cc", "100" }, []u8{2}, []?[]const u8{"100"});
420}
421
422test "clap.core: bare" {
423 const params = []Param(u8) {
424 Param(u8).init(0, false, Names.bare("aa")),
425 Param(u8).init(1, false, Names.bare("bb")),
426 Param(u8).init(2, true, Names.bare("cc")),
427 };
428
429 testNoErr(params, [][]const u8 { "aa" }, []u8{0}, []?[]const u8{null});
430 testNoErr(params, [][]const u8 { "aa", "bb" }, []u8{0,1}, []?[]const u8{null,null});
431 testNoErr(params, [][]const u8 { "cc=100" }, []u8{2}, []?[]const u8{"100"});
432 testNoErr(params, [][]const u8 { "cc", "100" }, []u8{2}, []?[]const u8{"100"});
433}
434
435test "clap.core: none" {
436 const params = []Param(u8) {
437 Param(u8).init(0, true, Names.none()),
438 };
439
440 testNoErr(params, [][]const u8 { "aa" }, []u8{0}, []?[]const u8{"aa"});
441}
442
443test "clap.core: all" {
444 const params = []Param(u8) {
445 Param(u8).init(
446 0,
447 false,
448 Names{
449 .bare = "aa",
450 .short = 'a',
451 .long = "aa",
452 }
453 ),
454 Param(u8).init(
455 1,
456 false,
457 Names{
458 .bare = "bb",
459 .short = 'b',
460 .long = "bb",
461 }
462 ),
463 Param(u8).init(
464 2,
465 true,
466 Names{
467 .bare = "cc",
468 .short = 'c',
469 .long = "cc",
470 }
471 ),
472 Param(u8).init(3, true, Names.none()),
473 };
474
475 testNoErr(params, [][]const u8 { "-a" }, []u8{0}, []?[]const u8{null});
476 testNoErr(params, [][]const u8 { "-a", "-b" }, []u8{0,1}, []?[]const u8{null,null});
477 testNoErr(params, [][]const u8 { "-ab" }, []u8{0,1}, []?[]const u8{null,null});
478 testNoErr(params, [][]const u8 { "-c=100" }, []u8{2}, []?[]const u8{"100"});
479 testNoErr(params, [][]const u8 { "-c100" }, []u8{2}, []?[]const u8{"100"});
480 testNoErr(params, [][]const u8 { "-c", "100" }, []u8{2}, []?[]const u8{"100"});
481 testNoErr(params, [][]const u8 { "-abc", "100" }, []u8{0,1,2}, []?[]const u8{null,null,"100"});
482 testNoErr(params, [][]const u8 { "-abc=100" }, []u8{0,1,2}, []?[]const u8{null,null,"100"});
483 testNoErr(params, [][]const u8 { "-abc100" }, []u8{0,1,2}, []?[]const u8{null,null,"100"});
484 testNoErr(params, [][]const u8 { "--aa" }, []u8{0}, []?[]const u8{null});
485 testNoErr(params, [][]const u8 { "--aa", "--bb" }, []u8{0,1}, []?[]const u8{null,null});
486 testNoErr(params, [][]const u8 { "--cc=100" }, []u8{2}, []?[]const u8{"100"});
487 testNoErr(params, [][]const u8 { "--cc", "100" }, []u8{2}, []?[]const u8{"100"});
488 testNoErr(params, [][]const u8 { "aa" }, []u8{0}, []?[]const u8{null});
489 testNoErr(params, [][]const u8 { "aa", "bb" }, []u8{0,1}, []?[]const u8{null,null});
490 testNoErr(params, [][]const u8 { "cc=100" }, []u8{2}, []?[]const u8{"100"});
491 testNoErr(params, [][]const u8 { "cc", "100" }, []u8{2}, []?[]const u8{"100"});
492 testNoErr(params, [][]const u8 { "dd" }, []u8{3}, []?[]const u8{"dd"});
493}
diff --git a/src/extended.zig b/src/extended.zig
new file mode 100644
index 0000000..9427b83
--- /dev/null
+++ b/src/extended.zig
@@ -0,0 +1,264 @@
1pub const core = @import("core.zig");
2
3const builtin = @import("builtin");
4const std = @import("std");
5
6const mem = std.mem;
7const fmt = std.fmt;
8const debug = std.debug;
9const io = std.io;
10
11const assert = debug.assert;
12
13const Opaque = @OpaqueType();
14
15pub const Param = struct {
16 field: []const u8,
17 short: ?u8,
18 long: ?[]const u8,
19 takes_value: ?Parser,
20 required: bool,
21 position: ?usize,
22
23 pub fn short(s: u8) Param {
24 return Param{
25 .field = []u8{s},
26 .short = s,
27 .long = null,
28 .takes_value = null,
29 .required = false,
30 .position = null,
31 };
32 }
33
34 pub fn long(l: []const u8) Param {
35 return Param{
36 .field = l,
37 .short = null,
38 .long = l,
39 .takes_value = null,
40 .required = false,
41 .position = null,
42 };
43 }
44
45 pub fn value(f: []const u8) Param {
46 return Param{
47 .field = f,
48 .short = null,
49 .long = null,
50 .takes_value = null,
51 .required = false,
52 .position = null,
53 };
54 }
55
56 /// Initialize a ::Param.
57 /// If ::name.len == 0, then it's a value parameter: "value".
58 /// If ::name.len == 1, then it's a short parameter: "-s".
59 /// If ::name.len > 1, then it's a long parameter: "--long".
60 pub fn smart(name: []const u8) Param {
61 return Param{
62 .field = name,
63 .short = if (name.len == 1) name[0] else null,
64 .long = if (name.len > 1) name else null,
65 .takes_value = null,
66 .required = false,
67 .position = null,
68 };
69 }
70
71 pub fn with(param: &const Param, comptime field_name: []const u8, v: var) Param {
72 var res = param.*;
73 @field(res, field_name) = v;
74 return res;
75 }
76};
77
78pub const Command = struct {
79 field: []const u8,
80 name: []const u8,
81 params: []const Param,
82 sub_commands: []const Command,
83
84 Result: type,
85 defaults: &const Opaque,
86 parent: ?&const Command,
87
88 pub fn init(name: []const u8, comptime Result: type, defaults: &const Result, params: []const Param, sub_commands: []const Command) Command {
89 return Command{
90 .field = name,
91 .name = name,
92 .params = params,
93 .sub_commands = sub_commands,
94 .Result = Result,
95 .defaults = @ptrCast(&const Opaque, defaults),
96 .parent = null,
97 };
98 }
99
100 pub fn with(command: &const Command, comptime field_name: []const u8, v: var) Param {
101 var res = command.*;
102 @field(res, field_name) = v;
103 return res;
104 }
105
106 pub fn parse(comptime command: &const Command, allocator: &mem.Allocator, arg_iter: &core.ArgIterator) !command.Result {
107 const Parent = struct {};
108 var parent = Parent{};
109 return command.parseHelper(&parent, allocator, arg_iter);
110 }
111
112 fn parseHelper(comptime command: &const Command, parent: var, allocator: &mem.Allocator, arg_iter: &core.ArgIterator) !command.Result {
113 const Result = struct {
114 parent: @typeOf(parent),
115 result: command.Result,
116 };
117
118 var result = Result{
119 .parent = parent,
120 .result = @ptrCast(&const command.Result, command.defaults).*,
121 };
122
123 // In order for us to wrap the core api, we have to translate clap.Param into core.Param.
124 const core_params = comptime blk: {
125 var res: [command.params.len + command.sub_commands.len]core.Param(usize) = undefined;
126
127 for (command.params) |p, i| {
128 const id = i;
129 res[id] = core.Param(usize) {
130 .id = id,
131 .takes_value = p.takes_value != null,
132 .names = core.Names{
133 .bare = null,
134 .short = p.short,
135 .long = p.long,
136 },
137 };
138 }
139
140 for (command.sub_commands) |c, i| {
141 const id = i + command.params.len;
142 res[id] = core.Param(usize) {
143 .id = id,
144 .takes_value = false,
145 .names = core.Names.bare(c.name),
146 };
147 }
148
149 break :blk res;
150 };
151
152 var handled = comptime blk: {
153 var res: [command.params.len]bool = undefined;
154 for (command.params) |p, i| {
155 res[i] = !p.required;
156 }
157
158 break :blk res;
159 };
160
161 var pos: usize = 0;
162 var iter = core.Clap(usize).init(core_params, arg_iter, allocator);
163 defer iter.deinit();
164
165 arg_loop:
166 while (try iter.next()) |arg| : (pos += 1) {
167 inline for(command.params) |param, i| {
168 comptime const field = "result." ++ param.field;
169
170 if (arg.id == i and (param.position ?? pos) == pos) {
171 if (param.takes_value) |parser| {
172 try parser.parse(getFieldPtr(&result, field), ??arg.value);
173 } else {
174 getFieldPtr(&result, field).* = true;
175 }
176 handled[i] = true;
177 continue :arg_loop;
178 }
179 }
180
181 inline for(command.sub_commands) |c, i| {
182 comptime const field = "result." ++ c.field;
183 comptime var sub_command = c;
184 sub_command.parent = command;
185
186 if (arg.id == i + command.params.len) {
187 getFieldPtr(&result, field).* = try sub_command.parseHelper(&result, allocator, arg_iter);
188 continue :arg_loop;
189 }
190 }
191
192 return error.InvalidArgument;
193 }
194
195 return result.result;
196 }
197
198 fn GetFieldPtrReturn(comptime Struct: type, comptime field: []const u8) type {
199 var inst: Struct = undefined;
200 const dot_index = comptime mem.indexOfScalar(u8, field, '.') ?? {
201 return @typeOf(&@field(inst, field));
202 };
203
204 return GetFieldPtrReturn(@typeOf(@field(inst, field[0..dot_index])), field[dot_index + 1..]);
205 }
206
207 fn getFieldPtr(curr: var, comptime field: []const u8) GetFieldPtrReturn(@typeOf(curr).Child, field) {
208 const dot_index = comptime mem.indexOfScalar(u8, field, '.') ?? {
209 return &@field(curr, field);
210 };
211
212 return getFieldPtr(&@field(curr, field[0..dot_index]), field[dot_index + 1..]);
213 }
214};
215
216pub const Parser = struct {
217 const UnsafeFunction = &const void;
218
219 FieldType: type,
220 Errors: type,
221 func: UnsafeFunction,
222
223 pub fn init(comptime FieldType: type, comptime Errors: type, func: parseFunc(FieldType, Errors)) Parser {
224 return Parser {
225 .FieldType = FieldType,
226 .Errors = Errors,
227 .func = @ptrCast(UnsafeFunction, func),
228 };
229 }
230
231 fn parse(comptime parser: Parser, field_ptr: TakePtr(parser.FieldType), arg: []const u8) parser.Errors!void {
232 return @ptrCast(parseFunc(parser.FieldType, parser.Errors), parser.func)(field_ptr, arg);
233 }
234
235 // TODO: This is a workaround, since we don't have pointer reform yet.
236 fn TakePtr(comptime T: type) type { return &T; }
237
238 fn parseFunc(comptime FieldType: type, comptime Errors: type) type {
239 return fn(&FieldType, []const u8) Errors!void;
240 }
241
242 pub fn int(comptime Int: type, comptime radix: u8) Parser {
243 const func = struct {
244 fn i(field_ptr: &Int, arg: []const u8) !void {
245 field_ptr.* = try fmt.parseInt(Int, arg, radix);
246 }
247 }.i;
248 return Parser.init(
249 Int,
250 @typeOf(func).ReturnType.ErrorSet,
251 func
252 );
253 }
254
255 const string = Parser.init(
256 []const u8,
257 error{},
258 struct {
259 fn s(field_ptr: &[]const u8, arg: []const u8) (error{}!void) {
260 field_ptr.* = arg;
261 }
262 }.s
263 );
264};