summaryrefslogtreecommitdiff
path: root/clap/comptime.zig
diff options
context:
space:
mode:
authorGravatar Asherah Connor2020-08-28 10:47:14 +1000
committerGravatar Komari Spaghetti2020-08-28 09:43:42 +0200
commitfc823f7a14a9bec930a816e4a4ccae0ec13d0f17 (patch)
treed9613980e493012c4e3a2bbe58ce66d6e8070a8d /clap/comptime.zig
parentparse and validate multiple option (diff)
downloadzig-clap-fc823f7a14a9bec930a816e4a4ccae0ec13d0f17.tar.gz
zig-clap-fc823f7a14a9bec930a816e4a4ccae0ec13d0f17.tar.xz
zig-clap-fc823f7a14a9bec930a816e4a4ccae0ec13d0f17.zip
separate options into single and multiple
This avoids allocations if you never use multiple arguments.
Diffstat (limited to 'clap/comptime.zig')
-rw-r--r--clap/comptime.zig60
1 files changed, 35 insertions, 25 deletions
diff --git a/clap/comptime.zig b/clap/comptime.zig
index cecfcb2..edbaafb 100644
--- a/clap/comptime.zig
+++ b/clap/comptime.zig
@@ -8,12 +8,17 @@ const debug = std.debug;
8 8
9pub fn ComptimeClap(comptime Id: type, comptime params: []const clap.Param(Id)) type { 9pub fn ComptimeClap(comptime Id: type, comptime params: []const clap.Param(Id)) type {
10 var flags: usize = 0; 10 var flags: usize = 0;
11 var options: usize = 0; 11 var single_options: usize = 0;
12 var multi_options: usize = 0;
12 var converted_params: []const clap.Param(usize) = &[_]clap.Param(usize){}; 13 var converted_params: []const clap.Param(usize) = &[_]clap.Param(usize){};
13 for (params) |param| { 14 for (params) |param| {
14 var index: usize = 0; 15 var index: usize = 0;
15 if (param.names.long != null or param.names.short != null) { 16 if (param.names.long != null or param.names.short != null) {
16 const ptr = if (param.takes_value != .None) &options else &flags; 17 const ptr = switch (param.takes_value) {
18 .None => &flags,
19 .One => &single_options,
20 .Many => &multi_options,
21 };
17 index = ptr.*; 22 index = ptr.*;
18 ptr.* += 1; 23 ptr.* += 1;
19 } 24 }
@@ -27,22 +32,27 @@ pub fn ComptimeClap(comptime Id: type, comptime params: []const clap.Param(Id))
27 } 32 }
28 33
29 return struct { 34 return struct {
30 options: [options]std.ArrayList([]const u8), 35 single_options: [single_options]?[]const u8,
36 multi_options: [multi_options][]const []const u8,
31 flags: [flags]bool, 37 flags: [flags]bool,
32 pos: []const []const u8, 38 pos: []const []const u8,
33 allocator: *mem.Allocator, 39 allocator: *mem.Allocator,
34 40
35 pub fn parse(allocator: *mem.Allocator, comptime ArgIter: type, iter: *ArgIter) !@This() { 41 pub fn parse(allocator: *mem.Allocator, comptime ArgIter: type, iter: *ArgIter) !@This() {
42 var multis = [_]std.ArrayList([]const u8){undefined} ** single_options;
43 for (multis) |*multi| {
44 multi.* = std.ArrayList([]const u8).init(allocator);
45 }
46
36 var pos = std.ArrayList([]const u8).init(allocator); 47 var pos = std.ArrayList([]const u8).init(allocator);
48
37 var res = @This(){ 49 var res = @This(){
38 .options = [_]std.ArrayList([]const u8){undefined} ** options, 50 .single_options = [_]?[]const u8{null} ** single_options,
51 .multi_options = [_][]const []const u8{undefined} ** multi_options,
39 .flags = [_]bool{false} ** flags, 52 .flags = [_]bool{false} ** flags,
40 .pos = undefined, 53 .pos = undefined,
41 .allocator = allocator, 54 .allocator = allocator,
42 }; 55 };
43 for (res.options) |*init_opt| {
44 init_opt.* = std.ArrayList([]const u8).init(allocator);
45 }
46 56
47 var stream = clap.StreamingClap(usize, ArgIter){ 57 var stream = clap.StreamingClap(usize, ArgIter){
48 .params = converted_params, 58 .params = converted_params,
@@ -52,28 +62,29 @@ pub fn ComptimeClap(comptime Id: type, comptime params: []const clap.Param(Id))
52 const param = arg.param; 62 const param = arg.param;
53 if (param.names.long == null and param.names.short == null) { 63 if (param.names.long == null and param.names.short == null) {
54 try pos.append(arg.value.?); 64 try pos.append(arg.value.?);
55 } else if (param.takes_value != .None) { 65 } else if (param.takes_value == .One) {
56 // If we don't have any optional parameters, then this code should 66 debug.assert(res.single_options.len != 0);
57 // never be reached. 67 res.single_options[param.id] = arg.value.?;
58 debug.assert(res.options.len != 0); 68 } else if (param.takes_value == .Many) {
59 69 debug.assert(res.multi_options.len != 0);
60 // Hack: Utilize Zigs lazy analyzis to avoid a compiler error 70 try multis[param.id].append(arg.value.?);
61 if (res.options.len != 0)
62 try res.options[param.id].append(arg.value.?);
63 } else { 71 } else {
64 debug.assert(res.flags.len != 0); 72 debug.assert(res.flags.len != 0);
65 if (res.flags.len != 0) 73 res.flags[param.id] = true;
66 res.flags[param.id] = true;
67 } 74 }
68 } 75 }
69 76
77 for (multis) |*multi, i| {
78 res.multi_options[i] = multi.toOwnedSlice();
79 }
70 res.pos = pos.toOwnedSlice(); 80 res.pos = pos.toOwnedSlice();
81
71 return res; 82 return res;
72 } 83 }
73 84
74 pub fn deinit(parser: *@This()) void { 85 pub fn deinit(parser: *@This()) void {
75 for (parser.options) |o| 86 for (parser.multi_options) |o|
76 o.deinit(); 87 parser.allocator.free(o);
77 parser.allocator.free(parser.pos); 88 parser.allocator.free(parser.pos);
78 parser.* = undefined; 89 parser.* = undefined;
79 } 90 }
@@ -86,14 +97,14 @@ pub fn ComptimeClap(comptime Id: type, comptime params: []const clap.Param(Id))
86 return parser.flags[param.id]; 97 return parser.flags[param.id];
87 } 98 }
88 99
89 pub fn allOptions(parser: @This(), comptime name: []const u8) [][]const u8 { 100 pub fn options(parser: @This(), comptime name: []const u8) []const []const u8 {
90 const param = comptime findParam(name); 101 const param = comptime findParam(name);
91 if (param.takes_value == .None) 102 if (param.takes_value == .None)
92 @compileError(name ++ " is a flag and not an option."); 103 @compileError(name ++ " is a flag and not an option.");
93 if (param.takes_value == .One) 104 if (param.takes_value == .One)
94 @compileError(name ++ " takes one option, not multiple."); 105 @compileError(name ++ " takes one option, not multiple.");
95 106
96 return parser.options[param.id].items; 107 return parser.multi_options[param.id];
97 } 108 }
98 109
99 pub fn option(parser: @This(), comptime name: []const u8) ?[]const u8 { 110 pub fn option(parser: @This(), comptime name: []const u8) ?[]const u8 {
@@ -102,8 +113,7 @@ pub fn ComptimeClap(comptime Id: type, comptime params: []const clap.Param(Id))
102 @compileError(name ++ " is a flag and not an option."); 113 @compileError(name ++ " is a flag and not an option.");
103 if (param.takes_value == .Many) 114 if (param.takes_value == .Many)
104 @compileError(name ++ " takes many options, not one."); 115 @compileError(name ++ " takes many options, not one.");
105 const items = parser.options[param.id].items; 116 return parser.single_options[param.id];
106 return if (items.len > 0) items[0] else null;
107 } 117 }
108 118
109 pub fn positionals(parser: @This()) []const []const u8 { 119 pub fn positionals(parser: @This()) []const []const u8 {
@@ -158,6 +168,6 @@ test "clap.comptime.ComptimeClap" {
158 testing.expectEqualStrings("0", args.option("--cc").?); 168 testing.expectEqualStrings("0", args.option("--cc").?);
159 testing.expectEqual(@as(usize, 1), args.positionals().len); 169 testing.expectEqual(@as(usize, 1), args.positionals().len);
160 testing.expectEqualStrings("something", args.positionals()[0]); 170 testing.expectEqualStrings("something", args.positionals()[0]);
161 testing.expectEqualSlices([]const u8, &[_][]const u8{ "a", "b" }, args.allOptions("-d")); 171 testing.expectEqualSlices([]const u8, &[_][]const u8{ "a", "b" }, args.options("-d"));
162 testing.expectEqualSlices([]const u8, &[_][]const u8{ "a", "b" }, args.allOptions("--dd")); 172 testing.expectEqualSlices([]const u8, &[_][]const u8{ "a", "b" }, args.options("--dd"));
163} 173}