summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md37
-rw-r--r--build.zig1
-rw-r--r--clap.zig249
-rw-r--r--example/README.md.template4
-rw-r--r--example/comptime-clap.zig19
-rw-r--r--example/help.zig14
-rw-r--r--src/comptime.zig26
7 files changed, 230 insertions, 120 deletions
diff --git a/README.md b/README.md
index 511087d..1de7f05 100644
--- a/README.md
+++ b/README.md
@@ -91,18 +91,11 @@ pub fn main() !void {
91 const allocator = std.heap.direct_allocator; 91 const allocator = std.heap.direct_allocator;
92 92
93 // First we specify what parameters our program can take. 93 // First we specify what parameters our program can take.
94 const params = [_]clap.Param([]const u8){ 94 // We can use `parseParam` parse a string to a `Param(Help)`
95 clap.Param([]const u8){ 95 const params = comptime [_]clap.Param(clap.Help){
96 .id = "Display this help and exit.", 96 clap.parseParam("-h, --help Display this help and exit. ") catch unreachable,
97 .names = clap.Names{ .short = 'h', .long = "help" }, 97 clap.parseParam("-n, --number=NUM An option parameter, which takes a value.") catch unreachable,
98 }, 98 clap.Param(clap.Help){
99 clap.Param([]const u8){
100 .id = "An option parameter, which takes a value.",
101 .names = clap.Names{ .short = 'n', .long = "number" },
102 .takes_value = true,
103 },
104 clap.Param([]const u8){
105 .id = "",
106 .takes_value = true, 99 .takes_value = true,
107 }, 100 },
108 }; 101 };
@@ -116,7 +109,7 @@ pub fn main() !void {
116 const exe = try iter.next(); 109 const exe = try iter.next();
117 110
118 // Finally we can parse the arguments 111 // Finally we can parse the arguments
119 var args = try clap.ComptimeClap([]const u8, params).parse(allocator, clap.args.OsIterator, &iter); 112 var args = try clap.ComptimeClap(clap.Help, params).parse(allocator, clap.args.OsIterator, &iter);
120 defer args.deinit(); 113 defer args.deinit();
121 114
122 if (args.flag("--help")) 115 if (args.flag("--help"))
@@ -187,19 +180,13 @@ pub fn main() !void {
187 const stderr = &stderr_out_stream.stream; 180 const stderr = &stderr_out_stream.stream;
188 181
189 // clap.help is a function that can print a simple help message, given a 182 // clap.help is a function that can print a simple help message, given a
190 // slice of Param([]const u8). There is also a helpEx, which can print a 183 // slice of Param(Help). There is also a helpEx, which can print a
191 // help message for any Param, but it is more verbose to call. 184 // help message for any Param, but it is more verbose to call.
192 try clap.help( 185 try clap.help(
193 stderr, 186 stderr,
194 [_]clap.Param([]const u8){ 187 comptime [_]clap.Param(clap.Help){
195 clap.Param([]const u8){ 188 clap.parseParam("-h, --help Display this help and exit. ") catch unreachable,
196 .id = "Display this help and exit.", 189 clap.parseParam("-v, --version Output version information and exit.") catch unreachable,
197 .names = clap.Names{ .short = 'h', .long = "help" },
198 },
199 clap.Param([]const u8){
200 .id = "Output version information and exit.",
201 .names = clap.Names{ .short = 'v', .long = "version" },
202 },
203 }, 190 },
204 ); 191 );
205} 192}
@@ -211,8 +198,8 @@ pub fn main() !void {
211 -v, --version Output version information and exit. 198 -v, --version Output version information and exit.
212``` 199```
213 200
214The `help` function is the simplest to call. It only takes an `OutStream` and a slice of 201The `help` functions are the simplest to call. It only takes an `OutStream` and a slice of
215`Param([]const u8)`. This function assumes that the id of each parameter is the help message. 202`Param(Help)`.
216 203
217The `helpEx` is the generic version of `help`. It can print a help message for any 204The `helpEx` is the generic version of `help`. It can print a help message for any
218`Param` give that the caller provides functions for getting the help and value strings. 205`Param` give that the caller provides functions for getting the help and value strings.
diff --git a/build.zig b/build.zig
index ec8579b..b9ec73a 100644
--- a/build.zig
+++ b/build.zig
@@ -17,6 +17,7 @@ pub fn build(b: *Builder) void {
17 const example = b.addExecutable(example_name, "example/" ++ example_name ++ ".zig"); 17 const example = b.addExecutable(example_name, "example/" ++ example_name ++ ".zig");
18 example.addPackagePath("clap", "clap.zig"); 18 example.addPackagePath("clap", "clap.zig");
19 example.setBuildMode(mode); 19 example.setBuildMode(mode);
20 example.install();
20 example_step.dependOn(&example.step); 21 example_step.dependOn(&example.step);
21 } 22 }
22 23
diff --git a/clap.zig b/clap.zig
index c16570f..fe5a5b0 100644
--- a/clap.zig
+++ b/clap.zig
@@ -3,6 +3,7 @@ const std = @import("std");
3const debug = std.debug; 3const debug = std.debug;
4const io = std.io; 4const io = std.io;
5const mem = std.mem; 5const mem = std.mem;
6const testing = std.testing;
6 7
7pub const args = @import("src/args.zig"); 8pub const args = @import("src/args.zig");
8 9
@@ -53,10 +54,166 @@ pub fn Param(comptime Id: type) type {
53 }; 54 };
54} 55}
55 56
57/// Takes a string and parses it to a Param(Help).
58/// This is the reverse of 'help2' but for at single parameter only.
59pub fn parseParam(line: []const u8) !Param(Help) {
60 var res = Param(Help){
61 .id = Help{
62 .msg = line[0..0],
63 .value = line[0..0],
64 },
65 };
66
67 var it = mem.tokenize(line, " \t");
68 var param_str = it.next() orelse return error.NoParamFound;
69 if (!mem.startsWith(u8, param_str, "--") and mem.startsWith(u8, param_str, "-")) {
70 const found_comma = param_str[param_str.len - 1] == ',';
71 if (found_comma)
72 param_str = param_str[0..param_str.len - 1];
73
74 switch (param_str.len) {
75 1 => return error.InvalidShortParam,
76 2 => {
77 res.names.short = param_str[1];
78 if (!found_comma) {
79 res.id.msg = mem.trim(u8, it.rest(), " \t");
80 return res;
81 }
82 },
83 else => {
84 res.names.short = param_str[1];
85 if (param_str[2] != '=')
86 return error.InvalidShortParam;
87
88 res.id.value = param_str[3..];
89 res.takes_value = true;
90
91 if (found_comma)
92 return error.TrailingComma;
93
94 res.id.msg = mem.trim(u8, it.rest(), " \t");
95 return res;
96 },
97 }
98
99 param_str = it.next() orelse return error.NoParamFound;
100 }
101
102 if (mem.startsWith(u8, param_str, "--")) {
103 res.names.long = param_str[2..];
104 if (mem.indexOfScalar(u8, param_str, '=')) |eql_index| {
105 res.names.long = param_str[2..eql_index];
106 res.id.value = param_str[eql_index + 1 ..];
107 res.takes_value = true;
108 }
109
110 if (param_str[param_str.len - 1] == ',')
111 return error.TrailingComma;
112
113 res.id.msg = mem.trim(u8, it.rest(), " \t");
114 return res;
115 }
116
117 return error.NoParamFound;
118}
119
120test "parseParam" {
121 var text: []const u8 = "-s, --long=value Help text";
122 testing.expectEqual(Param(Help){
123 .id = Help{
124 .msg = text[17..],
125 .value = text[11..16],
126 },
127 .names = Names{
128 .short = 's',
129 .long = text[6..10],
130 },
131 .takes_value = true,
132 }, try parseParam(text));
133
134 text = "--long=value Help text";
135 testing.expectEqual(Param(Help){
136 .id = Help{
137 .msg = text[13..],
138 .value = text[7..12],
139 },
140 .names = Names{
141 .short = null,
142 .long = text[2..6],
143 },
144 .takes_value = true,
145 }, try parseParam(text));
146
147 text = "-s=value Help text";
148 testing.expectEqual(Param(Help){
149 .id = Help{
150 .msg = text[9..],
151 .value = text[3..8],
152 },
153 .names = Names{
154 .short = 's',
155 .long = null,
156 },
157 .takes_value = true,
158 }, try parseParam(text));
159
160 text = "-s, --long Help text";
161 testing.expectEqual(Param(Help){
162 .id = Help{
163 .msg = text[11..],
164 .value = text[0..0],
165 },
166 .names = Names{
167 .short = 's',
168 .long = text[6..10],
169 },
170 .takes_value = false,
171 }, try parseParam(text));
172
173 text = "-s Help text";
174 testing.expectEqual(Param(Help){
175 .id = Help{
176 .msg = text[3..],
177 .value = text[0..0],
178 },
179 .names = Names{
180 .short = 's',
181 .long = null,
182 },
183 .takes_value = false,
184 }, try parseParam(text));
185
186 text = "--long Help text";
187 testing.expectEqual(Param(Help){
188 .id = Help{
189 .msg = text[7..],
190 .value = text[0..0],
191 },
192 .names = Names{
193 .short = null,
194 .long = text[2..6],
195 },
196 .takes_value = false,
197 }, try parseParam(text));
198
199 testing.expectError(error.NoParamFound, parseParam("Help"));
200 testing.expectError(error.TrailingComma, parseParam("--long, Help"));
201 testing.expectError(error.TrailingComma, parseParam("--long=value, Help"));
202 testing.expectError(error.NoParamFound, parseParam("-s, Help"));
203 testing.expectError(error.TrailingComma, parseParam("-s=value, Help"));
204 testing.expectError(error.InvalidShortParam, parseParam("-ss Help"));
205 testing.expectError(error.InvalidShortParam, parseParam("-ss=value Help"));
206 testing.expectError(error.InvalidShortParam, parseParam("- Help"));
207}
208
209
210
56/// Will print a help message in the following format: 211/// Will print a help message in the following format:
57/// -s, --long=value_text help_text 212/// -s, --long=value_text help_text
58/// -s, help_text 213/// -s, help_text
214/// -s=value_text help_text
59/// --long help_text 215/// --long help_text
216/// --long=value_text help_text
60pub fn helpFull( 217pub fn helpFull(
61 stream: var, 218 stream: var,
62 comptime Id: type, 219 comptime Id: type,
@@ -152,18 +309,22 @@ pub fn helpEx(
152 ); 309 );
153} 310}
154 311
155/// A wrapper around helpEx that takes a Param([]const u8) and uses the string id 312pub const Help = struct {
156/// as the help text for each paramter. 313 msg: []const u8 = "",
157pub fn help(stream: var, params: []const Param([]const u8)) !void { 314 value: []const u8 = "",
158 try helpEx(stream, []const u8, params, getHelpSimple, getValueSimple); 315};
316
317/// A wrapper around helpEx that takes a Param(Help).
318pub fn help(stream: var, params: []const Param(Help)) !void {
319 try helpEx(stream, Help, params, getHelpSimple, getValueSimple);
159} 320}
160 321
161fn getHelpSimple(param: Param([]const u8)) []const u8 { 322fn getHelpSimple(param: Param(Help)) []const u8 {
162 return param.id; 323 return param.id.msg;
163} 324}
164 325
165fn getValueSimple(param: Param([]const u8)) []const u8 { 326fn getValueSimple(param: Param(Help)) []const u8 {
166 return "VALUE"; 327 return param.id.value;
167} 328}
168 329
169test "clap.help" { 330test "clap.help" {
@@ -171,54 +332,44 @@ test "clap.help" {
171 var slice_stream = io.SliceOutStream.init(buf[0..]); 332 var slice_stream = io.SliceOutStream.init(buf[0..]);
172 try help( 333 try help(
173 &slice_stream.stream, 334 &slice_stream.stream,
174 [_]Param([]const u8){ 335 comptime [_]Param(Help){
175 Param([]const u8){ 336 parseParam("-a Short flag. ") catch unreachable,
176 .id = "Short flag.", 337 parseParam("-b=V1 Short option.") catch unreachable,
177 .names = Names{ .short = 'a' }, 338 parseParam("--aa Long flag. ") catch unreachable,
178 }, 339 parseParam("--bb=V2 Long option. ") catch unreachable,
179 Param([]const u8){ 340 parseParam("-c, --cc Both flag. ") catch unreachable,
180 .id = "Short option.", 341 parseParam("-d, --dd=V3 Both option. ") catch unreachable,
181 .names = Names{ .short = 'b' }, 342 Param(Help){
182 .takes_value = true, 343 .id = Help{
183 }, 344 .msg = "Positional. This should not appear in the help message.",
184 Param([]const u8){ 345 },
185 .id = "Long flag.",
186 .names = Names{ .long = "aa" },
187 },
188 Param([]const u8){
189 .id = "Long option.",
190 .names = Names{ .long = "bb" },
191 .takes_value = true,
192 },
193 Param([]const u8){
194 .id = "Both flag.",
195 .names = Names{ .short = 'c', .long = "cc" },
196 },
197 Param([]const u8){
198 .id = "Both option.",
199 .names = Names{ .short = 'd', .long = "dd" },
200 .takes_value = true,
201 },
202 Param([]const u8){
203 .id = "Positional. This should not appear in the help message.",
204 .takes_value = true, 346 .takes_value = true,
205 }, 347 },
206 }, 348 },
207 ); 349 );
208 350
209 const expected = "" ++ 351 const expected = "" ++
210 "\t-a \tShort flag.\n" ++ 352 "\t-a \tShort flag.\n" ++
211 "\t-b=VALUE \tShort option.\n" ++ 353 "\t-b=V1 \tShort option.\n" ++
212 "\t --aa \tLong flag.\n" ++ 354 "\t --aa \tLong flag.\n" ++
213 "\t --bb=VALUE\tLong option.\n" ++ 355 "\t --bb=V2\tLong option.\n" ++
214 "\t-c, --cc \tBoth flag.\n" ++ 356 "\t-c, --cc \tBoth flag.\n" ++
215 "\t-d, --dd=VALUE\tBoth option.\n"; 357 "\t-d, --dd=V3\tBoth option.\n";
216 358
217 if (!mem.eql(u8, slice_stream.getWritten(), expected)) { 359 const actual = slice_stream.getWritten();
218 debug.warn("============ Expected ============\n"); 360 if (!mem.eql(u8, actual, expected)) {
361 debug.warn("\n============ Expected ============\n");
219 debug.warn("{}", expected); 362 debug.warn("{}", expected);
220 debug.warn("============= Actual =============\n"); 363 debug.warn("============= Actual =============\n");
221 debug.warn("{}", slice_stream.getWritten()); 364 debug.warn("{}", actual);
222 return error.NoMatch; 365
366 var buffer: [1024 * 2]u8 = undefined;
367 var fba = std.heap.FixedBufferAllocator.init(&buffer);
368
369 debug.warn("============ Expected (escaped) ============\n");
370 debug.warn("{x}\n", expected);
371 debug.warn("============ Actual (escaped) ============\n");
372 debug.warn("{x}\n", actual);
373 testing.expect(false);
223 } 374 }
224} 375}
diff --git a/example/README.md.template b/example/README.md.template
index 88914fb..446fd78 100644
--- a/example/README.md.template
+++ b/example/README.md.template
@@ -67,8 +67,8 @@ program can take.
67 -v, --version Output version information and exit. 67 -v, --version Output version information and exit.
68``` 68```
69 69
70The `help` function is the simplest to call. It only takes an `OutStream` and a slice of 70The `help` functions are the simplest to call. It only takes an `OutStream` and a slice of
71`Param([]const u8)`. This function assumes that the id of each parameter is the help message. 71`Param(Help)`.
72 72
73The `helpEx` is the generic version of `help`. It can print a help message for any 73The `helpEx` is the generic version of `help`. It can print a help message for any
74`Param` give that the caller provides functions for getting the help and value strings. 74`Param` give that the caller provides functions for getting the help and value strings.
diff --git a/example/comptime-clap.zig b/example/comptime-clap.zig
index 0b6d2c4..f73da69 100644
--- a/example/comptime-clap.zig
+++ b/example/comptime-clap.zig
@@ -7,18 +7,11 @@ pub fn main() !void {
7 const allocator = std.heap.direct_allocator; 7 const allocator = std.heap.direct_allocator;
8 8
9 // First we specify what parameters our program can take. 9 // First we specify what parameters our program can take.
10 const params = [_]clap.Param([]const u8){ 10 // We can use `parseParam` parse a string to a `Param(Help)`
11 clap.Param([]const u8){ 11 const params = comptime [_]clap.Param(clap.Help){
12 .id = "Display this help and exit.", 12 clap.parseParam("-h, --help Display this help and exit. ") catch unreachable,
13 .names = clap.Names{ .short = 'h', .long = "help" }, 13 clap.parseParam("-n, --number=NUM An option parameter, which takes a value.") catch unreachable,
14 }, 14 clap.Param(clap.Help){
15 clap.Param([]const u8){
16 .id = "An option parameter, which takes a value.",
17 .names = clap.Names{ .short = 'n', .long = "number" },
18 .takes_value = true,
19 },
20 clap.Param([]const u8){
21 .id = "",
22 .takes_value = true, 15 .takes_value = true,
23 }, 16 },
24 }; 17 };
@@ -32,7 +25,7 @@ pub fn main() !void {
32 const exe = try iter.next(); 25 const exe = try iter.next();
33 26
34 // Finally we can parse the arguments 27 // Finally we can parse the arguments
35 var args = try clap.ComptimeClap([]const u8, params).parse(allocator, clap.args.OsIterator, &iter); 28 var args = try clap.ComptimeClap(clap.Help, params).parse(allocator, clap.args.OsIterator, &iter);
36 defer args.deinit(); 29 defer args.deinit();
37 30
38 if (args.flag("--help")) 31 if (args.flag("--help"))
diff --git a/example/help.zig b/example/help.zig
index 35c0258..de8a55a 100644
--- a/example/help.zig
+++ b/example/help.zig
@@ -7,19 +7,13 @@ pub fn main() !void {
7 const stderr = &stderr_out_stream.stream; 7 const stderr = &stderr_out_stream.stream;
8 8
9 // clap.help is a function that can print a simple help message, given a 9 // clap.help is a function that can print a simple help message, given a
10 // slice of Param([]const u8). There is also a helpEx, which can print a 10 // slice of Param(Help). There is also a helpEx, which can print a
11 // help message for any Param, but it is more verbose to call. 11 // help message for any Param, but it is more verbose to call.
12 try clap.help( 12 try clap.help(
13 stderr, 13 stderr,
14 [_]clap.Param([]const u8){ 14 comptime [_]clap.Param(clap.Help){
15 clap.Param([]const u8){ 15 clap.parseParam("-h, --help Display this help and exit. ") catch unreachable,
16 .id = "Display this help and exit.", 16 clap.parseParam("-v, --version Output version information and exit.") catch unreachable,
17 .names = clap.Names{ .short = 'h', .long = "help" },
18 },
19 clap.Param([]const u8){
20 .id = "Output version information and exit.",
21 .names = clap.Names{ .short = 'v', .long = "version" },
22 },
23 }, 17 },
24 ); 18 );
25} 19}
diff --git a/src/comptime.zig b/src/comptime.zig
index 3220ad6..8fd3c1d 100644
--- a/src/comptime.zig
+++ b/src/comptime.zig
@@ -113,27 +113,11 @@ pub fn ComptimeClap(comptime Id: type, comptime params: []const clap.Param(Id))
113} 113}
114 114
115test "clap.comptime.ComptimeClap" { 115test "clap.comptime.ComptimeClap" {
116 const Clap = ComptimeClap(void, [_]clap.Param(void){ 116 const Clap = ComptimeClap(clap.Help, comptime [_]clap.Param(clap.Help){
117 clap.Param(void){ 117 clap.parseParam("-a, --aa ") catch unreachable,
118 .names = clap.Names{ 118 clap.parseParam("-b, --bb ") catch unreachable,
119 .short = 'a', 119 clap.parseParam("-c, --cc=V") catch unreachable,
120 .long = "aa", 120 clap.Param(clap.Help){
121 },
122 },
123 clap.Param(void){
124 .names = clap.Names{
125 .short = 'b',
126 .long = "bb",
127 },
128 },
129 clap.Param(void){
130 .names = clap.Names{
131 .short = 'c',
132 .long = "cc",
133 },
134 .takes_value = true,
135 },
136 clap.Param(void){
137 .takes_value = true, 121 .takes_value = true,
138 }, 122 },
139 }); 123 });