diff options
| -rw-r--r-- | README.md | 137 | ||||
| -rw-r--r-- | build.zig | 2 | ||||
| -rw-r--r-- | clap.zig | 546 | ||||
| -rw-r--r-- | clap/comptime.zig | 237 | ||||
| -rw-r--r-- | clap/parsers.zig | 48 | ||||
| -rw-r--r-- | clap/streaming.zig | 34 | ||||
| -rw-r--r-- | example/README.md.template | 51 | ||||
| -rw-r--r-- | example/help.zig | 8 | ||||
| -rw-r--r-- | example/simple-error.zig | 13 | ||||
| -rw-r--r-- | example/simple-ex.zig | 39 | ||||
| -rw-r--r-- | example/simple.zig | 24 | ||||
| -rw-r--r-- | example/streaming-clap.zig | 2 | ||||
| -rw-r--r-- | example/usage.zig | 14 |
13 files changed, 570 insertions, 585 deletions
| @@ -34,75 +34,98 @@ pub fn main() !void { | |||
| 34 | // First we specify what parameters our program can take. | 34 | // First we specify what parameters our program can take. |
| 35 | // We can use `parseParam` to parse a string to a `Param(Help)` | 35 | // We can use `parseParam` to parse a string to a `Param(Help)` |
| 36 | const params = comptime [_]clap.Param(clap.Help){ | 36 | const params = comptime [_]clap.Param(clap.Help){ |
| 37 | clap.parseParam("-h, --help Display this help and exit. ") catch unreachable, | 37 | clap.parseParam("-h, --help Display this help and exit.") catch unreachable, |
| 38 | clap.parseParam("-n, --number <NUM> An option parameter, which takes a value.") catch unreachable, | 38 | clap.parseParam("-n, --number <usize> An option parameter, which takes a value.") catch unreachable, |
| 39 | clap.parseParam("-s, --string <STR>... An option parameter which can be specified multiple times.") catch unreachable, | 39 | clap.parseParam("-s, --string <str>... An option parameter which can be specified multiple times.") catch unreachable, |
| 40 | clap.parseParam("<POS>...") catch unreachable, | 40 | clap.parseParam("<str>...") catch unreachable, |
| 41 | }; | 41 | }; |
| 42 | 42 | ||
| 43 | // Initalize our diagnostics, which can be used for reporting useful errors. | 43 | // Initalize our diagnostics, which can be used for reporting useful errors. |
| 44 | // This is optional. You can also pass `.{}` to `clap.parse` if you don't | 44 | // This is optional. You can also pass `.{}` to `clap.parse` if you don't |
| 45 | // care about the extra information `Diagnostics` provides. | 45 | // care about the extra information `Diagnostics` provides. |
| 46 | var diag = clap.Diagnostic{}; | 46 | var diag = clap.Diagnostic{}; |
| 47 | var args = clap.parse(clap.Help, ¶ms, .{ .diagnostic = &diag }) catch |err| { | 47 | var res = clap.parse(clap.Help, ¶ms, clap.parsers.default, .{ |
| 48 | .diagnostic = &diag, | ||
| 49 | }) catch |err| { | ||
| 48 | // Report useful error and exit | 50 | // Report useful error and exit |
| 49 | diag.report(io.getStdErr().writer(), err) catch {}; | 51 | diag.report(io.getStdErr().writer(), err) catch {}; |
| 50 | return err; | 52 | return err; |
| 51 | }; | 53 | }; |
| 52 | defer args.deinit(); | 54 | defer res.deinit(); |
| 53 | 55 | ||
| 54 | if (args.flag("--help")) | 56 | if (res.args.help) |
| 55 | debug.print("--help\n", .{}); | 57 | debug.print("--help\n", .{}); |
| 56 | if (args.option("--number")) |n| | 58 | if (res.args.number) |n| |
| 57 | debug.print("--number = {s}\n", .{n}); | 59 | debug.print("--number = {}\n", .{n}); |
| 58 | for (args.options("--string")) |s| | 60 | for (res.args.string) |s| |
| 59 | debug.print("--string = {s}\n", .{s}); | 61 | debug.print("--string = {s}\n", .{s}); |
| 60 | for (args.positionals()) |pos| | 62 | for (res.positionals) |pos| |
| 61 | debug.print("{s}\n", .{pos}); | 63 | debug.print("{s}\n", .{pos}); |
| 62 | } | 64 | } |
| 63 | 65 | ||
| 64 | ``` | 66 | ``` |
| 65 | 67 | ||
| 66 | The data structure returned has lookup speed on par with array access (`arr[i]`) and validates | 68 | The result will contain an `args` field and a `positionals` field. `args` will have one field |
| 67 | that the strings you pass to `option`, `options` and `flag` are actually parameters that the | 69 | for each none positional parameter of your program. The name of the field will be the longest |
| 68 | program can take: | 70 | name of the parameter. |
| 71 | |||
| 72 | The fields in `args` are typed. The type is based on the name of the value the parameter takes. | ||
| 73 | Since `--number` takes a `usize` the field `res.args.number` has the type `usize`. | ||
| 74 | |||
| 75 | Note that this is only the case because `clap.parsers.default` has a field called `usize` which | ||
| 76 | contains a parser that returns `usize`. You can pass in something other than `clap.parsers.default` | ||
| 77 | if you want some other mapping. | ||
| 69 | 78 | ||
| 70 | ```zig | 79 | ```zig |
| 71 | const clap = @import("clap"); | 80 | const clap = @import("clap"); |
| 72 | const std = @import("std"); | 81 | const std = @import("std"); |
| 73 | 82 | ||
| 83 | const debug = std.debug; | ||
| 84 | const io = std.io; | ||
| 85 | const process = std.process; | ||
| 86 | |||
| 74 | pub fn main() !void { | 87 | pub fn main() !void { |
| 88 | // First we specify what parameters our program can take. | ||
| 89 | // We can use `parseParam` to parse a string to a `Param(Help)` | ||
| 75 | const params = comptime [_]clap.Param(clap.Help){ | 90 | const params = comptime [_]clap.Param(clap.Help){ |
| 76 | clap.parseParam("-h, --help Display this help and exit.") catch unreachable, | 91 | clap.parseParam("-h, --help Display this help and exit.") catch unreachable, |
| 92 | clap.parseParam("-n, --number <INT> An option parameter, which takes a value.") catch unreachable, | ||
| 93 | clap.parseParam("-s, --string <STR>... An option parameter which can be specified multiple times.") catch unreachable, | ||
| 94 | clap.parseParam("<FILE>...") catch unreachable, | ||
| 77 | }; | 95 | }; |
| 78 | 96 | ||
| 79 | var args = try clap.parse(clap.Help, ¶ms, .{}); | 97 | // Declare our own parsers which are used to map the argument strings to other |
| 80 | defer args.deinit(); | 98 | // types. |
| 99 | const parsers = comptime .{ | ||
| 100 | .STR = clap.parsers.string, | ||
| 101 | .FILE = clap.parsers.string, | ||
| 102 | .INT = clap.parsers.int(usize, 10), | ||
| 103 | }; | ||
| 81 | 104 | ||
| 82 | _ = args.flag("--helps"); | 105 | var diag = clap.Diagnostic{}; |
| 83 | } | 106 | var res = clap.parse(clap.Help, ¶ms, parsers, .{ |
| 107 | .diagnostic = &diag, | ||
| 108 | }) catch |err| { | ||
| 109 | diag.report(io.getStdErr().writer(), err) catch {}; | ||
| 110 | return err; | ||
| 111 | }; | ||
| 112 | defer res.deinit(); | ||
| 84 | 113 | ||
| 85 | ``` | 114 | if (res.args.help) |
| 115 | debug.print("--help\n", .{}); | ||
| 116 | if (res.args.number) |n| | ||
| 117 | debug.print("--number = {}\n", .{n}); | ||
| 118 | for (res.args.string) |s| | ||
| 119 | debug.print("--string = {s}\n", .{s}); | ||
| 120 | for (res.positionals) |pos| | ||
| 121 | debug.print("{s}\n", .{pos}); | ||
| 122 | } | ||
| 86 | 123 | ||
| 87 | ``` | 124 | ``` |
| 88 | zig-clap/clap/comptime.zig:109:17: error: --helps is not a parameter. | ||
| 89 | @compileError(name ++ " is not a parameter."); | ||
| 90 | ^ | ||
| 91 | zig-clap/clap/comptime.zig:77:45: note: called from here | ||
| 92 | const param = comptime findParam(name); | ||
| 93 | ^ | ||
| 94 | zig-clap/clap.zig:238:31: note: called from here | ||
| 95 | return a.clap.flag(name); | ||
| 96 | ^ | ||
| 97 | zig-clap/example/simple-error.zig:16:18: note: called from here | ||
| 98 | _ = args.flag("--helps"); | ||
| 99 | ``` | ||
| 100 | |||
| 101 | There is also a `parseEx` variant that takes an argument iterator. | ||
| 102 | 125 | ||
| 103 | ### `StreamingClap` | 126 | ### `streaming.Clap` |
| 104 | 127 | ||
| 105 | The `StreamingClap` is the base of all the other parsers. It's a streaming parser that uses an | 128 | The `streaming.Clap` is the base of all the other parsers. It's a streaming parser that uses an |
| 106 | `args.Iterator` to provide it with arguments lazily. | 129 | `args.Iterator` to provide it with arguments lazily. |
| 107 | 130 | ||
| 108 | ```zig | 131 | ```zig |
| @@ -140,7 +163,7 @@ pub fn main() !void { | |||
| 140 | // This is optional. You can also leave the `diagnostic` field unset if you | 163 | // This is optional. You can also leave the `diagnostic` field unset if you |
| 141 | // don't care about the extra information `Diagnostic` provides. | 164 | // don't care about the extra information `Diagnostic` provides. |
| 142 | var diag = clap.Diagnostic{}; | 165 | var diag = clap.Diagnostic{}; |
| 143 | var parser = clap.StreamingClap(u8, process.ArgIterator){ | 166 | var parser = clap.streaming.Clap(u8, process.ArgIterator){ |
| 144 | .params = ¶ms, | 167 | .params = ¶ms, |
| 145 | .iter = &iter, | 168 | .iter = &iter, |
| 146 | .diagnostic = &diag, | 169 | .diagnostic = &diag, |
| @@ -173,8 +196,9 @@ is generated at runtime. | |||
| 173 | 196 | ||
| 174 | ### `help` | 197 | ### `help` |
| 175 | 198 | ||
| 176 | The `help`, `helpEx` and `helpFull` are functions for printing a simple list of all parameters the | 199 | The `help` prints a simple list of all parameters the program can take. It expects the |
| 177 | program can take. | 200 | `Id` to have a `description` method and an `value` method so that it can provide that |
| 201 | in the output. | ||
| 178 | 202 | ||
| 179 | ```zig | 203 | ```zig |
| 180 | const clap = @import("clap"); | 204 | const clap = @import("clap"); |
| @@ -186,14 +210,14 @@ pub fn main() !void { | |||
| 186 | clap.parseParam("-v, --version Output version information and exit.") catch unreachable, | 210 | clap.parseParam("-v, --version Output version information and exit.") catch unreachable, |
| 187 | }; | 211 | }; |
| 188 | 212 | ||
| 189 | var args = try clap.parse(clap.Help, ¶ms, .{}); | 213 | var res = try clap.parse(clap.Help, ¶ms, clap.parsers.default, .{}); |
| 190 | defer args.deinit(); | 214 | defer res.deinit(); |
| 191 | 215 | ||
| 192 | // clap.help is a function that can print a simple help message, given a | 216 | // clap.help is a function that can print a simple help message, given a |
| 193 | // slice of Param(Help). There is also a helpEx, which can print a | 217 | // slice of Param(Help). There is also a helpEx, which can print a |
| 194 | // help message for any Param, but it is more verbose to call. | 218 | // help message for any Param, but it is more verbose to call. |
| 195 | if (args.flag("--help")) | 219 | if (res.args.help) |
| 196 | return clap.help(std.io.getStdErr().writer(), ¶ms); | 220 | return clap.help(std.io.getStdErr().writer(), clap.Help, ¶ms); |
| 197 | } | 221 | } |
| 198 | 222 | ||
| 199 | ``` | 223 | ``` |
| @@ -204,19 +228,10 @@ $ zig-out/bin/help --help | |||
| 204 | -v, --version Output version information and exit. | 228 | -v, --version Output version information and exit. |
| 205 | ``` | 229 | ``` |
| 206 | 230 | ||
| 207 | The `help` functions are the simplest to call. It only takes an `OutStream` and a slice of | ||
| 208 | `Param(Help)`. | ||
| 209 | |||
| 210 | The `helpEx` is the generic version of `help`. It can print a help message for any | ||
| 211 | `Param` give that the caller provides functions for getting the help and value strings. | ||
| 212 | |||
| 213 | The `helpFull` is even more generic, allowing the functions that get the help and value strings | ||
| 214 | to return errors and take a context as a parameter. | ||
| 215 | |||
| 216 | ### `usage` | 231 | ### `usage` |
| 217 | 232 | ||
| 218 | The `usage`, `usageEx` and `usageFull` are functions for printing a small abbreviated version | 233 | The `usage` prints a small abbreviated version of the help message. It expects the `Id` |
| 219 | of the help message. | 234 | to have a `value` method so it can provide that in the output. |
| 220 | 235 | ||
| 221 | ```zig | 236 | ```zig |
| 222 | const clap = @import("clap"); | 237 | const clap = @import("clap"); |
| @@ -224,19 +239,19 @@ const std = @import("std"); | |||
| 224 | 239 | ||
| 225 | pub fn main() !void { | 240 | pub fn main() !void { |
| 226 | const params = comptime [_]clap.Param(clap.Help){ | 241 | const params = comptime [_]clap.Param(clap.Help){ |
| 227 | clap.parseParam("-h, --help Display this help and exit. ") catch unreachable, | 242 | clap.parseParam("-h, --help Display this help and exit.") catch unreachable, |
| 228 | clap.parseParam("-v, --version Output version information and exit. ") catch unreachable, | 243 | clap.parseParam("-v, --version Output version information and exit.") catch unreachable, |
| 229 | clap.parseParam(" --value <N> An option parameter, which takes a value.") catch unreachable, | 244 | clap.parseParam(" --value <str> An option parameter, which takes a value.") catch unreachable, |
| 230 | }; | 245 | }; |
| 231 | 246 | ||
| 232 | var args = try clap.parse(clap.Help, ¶ms, .{}); | 247 | var res = try clap.parse(clap.Help, ¶ms, clap.parsers.default, .{}); |
| 233 | defer args.deinit(); | 248 | defer res.deinit(); |
| 234 | 249 | ||
| 235 | // clap.usage is a function that can print a simple usage message, given a | 250 | // clap.usage is a function that can print a simple usage message, given a |
| 236 | // slice of Param(Help). There is also a usageEx, which can print a | 251 | // slice of Param(Help). There is also a usageEx, which can print a |
| 237 | // usage message for any Param, but it is more verbose to call. | 252 | // usage message for any Param, but it is more verbose to call. |
| 238 | if (args.flag("--help")) | 253 | if (res.args.help) |
| 239 | return clap.usage(std.io.getStdErr().writer(), ¶ms); | 254 | return clap.usage(std.io.getStdErr().writer(), clap.Help, ¶ms); |
| 240 | } | 255 | } |
| 241 | 256 | ||
| 242 | ``` | 257 | ``` |
| @@ -60,7 +60,7 @@ fn readMeStep(b: *Builder) *std.build.Step { | |||
| 60 | const stream = file.writer(); | 60 | const stream = file.writer(); |
| 61 | try stream.print(@embedFile("example/README.md.template"), .{ | 61 | try stream.print(@embedFile("example/README.md.template"), .{ |
| 62 | @embedFile("example/simple.zig"), | 62 | @embedFile("example/simple.zig"), |
| 63 | @embedFile("example/simple-error.zig"), | 63 | @embedFile("example/simple-ex.zig"), |
| 64 | @embedFile("example/streaming-clap.zig"), | 64 | @embedFile("example/streaming-clap.zig"), |
| 65 | @embedFile("example/help.zig"), | 65 | @embedFile("example/help.zig"), |
| 66 | @embedFile("example/usage.zig"), | 66 | @embedFile("example/usage.zig"), |
| @@ -1,5 +1,6 @@ | |||
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | const builtin = std.builtin; | ||
| 3 | const debug = std.debug; | 4 | const debug = std.debug; |
| 4 | const heap = std.heap; | 5 | const heap = std.heap; |
| 5 | const io = std.io; | 6 | const io = std.io; |
| @@ -8,21 +9,37 @@ const process = std.process; | |||
| 8 | const testing = std.testing; | 9 | const testing = std.testing; |
| 9 | 10 | ||
| 10 | pub const args = @import("clap/args.zig"); | 11 | pub const args = @import("clap/args.zig"); |
| 12 | pub const parsers = @import("clap/parsers.zig"); | ||
| 13 | pub const streaming = @import("clap/streaming.zig"); | ||
| 11 | 14 | ||
| 12 | test "clap" { | 15 | test "clap" { |
| 13 | testing.refAllDecls(@This()); | 16 | testing.refAllDecls(@This()); |
| 14 | } | 17 | } |
| 15 | 18 | ||
| 16 | pub const ComptimeClap = @import("clap/comptime.zig").ComptimeClap; | 19 | /// The names a `Param` can have. |
| 17 | pub const StreamingClap = @import("clap/streaming.zig").StreamingClap; | ||
| 18 | |||
| 19 | /// The names a ::Param can have. | ||
| 20 | pub const Names = struct { | 20 | pub const Names = struct { |
| 21 | /// '-' prefix | 21 | /// '-' prefix |
| 22 | short: ?u8 = null, | 22 | short: ?u8 = null, |
| 23 | 23 | ||
| 24 | /// '--' prefix | 24 | /// '--' prefix |
| 25 | long: ?[]const u8 = null, | 25 | long: ?[]const u8 = null, |
| 26 | |||
| 27 | pub fn longest(names: *const Names) Longest { | ||
| 28 | if (names.long) |long| | ||
| 29 | return .{ .kind = .long, .name = long }; | ||
| 30 | if (names.short) |*short| { | ||
| 31 | // TODO: Zig cannot figure out @as(*const [1]u8, short) in the ano literal | ||
| 32 | const casted: *const [1]u8 = short; | ||
| 33 | return .{ .kind = .short, .name = casted }; | ||
| 34 | } | ||
| 35 | |||
| 36 | return .{ .kind = .positinal, .name = "" }; | ||
| 37 | } | ||
| 38 | |||
| 39 | pub const Longest = struct { | ||
| 40 | kind: enum { long, short, positinal }, | ||
| 41 | name: []const u8, | ||
| 42 | }; | ||
| 26 | }; | 43 | }; |
| 27 | 44 | ||
| 28 | /// Whether a param takes no value (a flag), one value, or can be specified multiple times. | 45 | /// Whether a param takes no value (a flag), one value, or can be specified multiple times. |
| @@ -124,18 +141,18 @@ fn parseParamRest(line: []const u8) Param(Help) { | |||
| 124 | return .{ | 141 | return .{ |
| 125 | .takes_value = if (takes_many) .many else .one, | 142 | .takes_value = if (takes_many) .many else .one, |
| 126 | .id = .{ | 143 | .id = .{ |
| 127 | .msg = mem.trim(u8, line[help_start..], " \t"), | 144 | .desc = mem.trim(u8, line[help_start..], " \t"), |
| 128 | .value = line[1..len], | 145 | .val = line[1..len], |
| 129 | }, | 146 | }, |
| 130 | }; | 147 | }; |
| 131 | } | 148 | } |
| 132 | 149 | ||
| 133 | return .{ .id = .{ .msg = mem.trim(u8, line, " \t") } }; | 150 | return .{ .id = .{ .desc = mem.trim(u8, line, " \t") } }; |
| 134 | } | 151 | } |
| 135 | 152 | ||
| 136 | fn expectParam(expect: Param(Help), actual: Param(Help)) !void { | 153 | fn expectParam(expect: Param(Help), actual: Param(Help)) !void { |
| 137 | try testing.expectEqualStrings(expect.id.msg, actual.id.msg); | 154 | try testing.expectEqualStrings(expect.id.desc, actual.id.desc); |
| 138 | try testing.expectEqualStrings(expect.id.value, actual.id.value); | 155 | try testing.expectEqualStrings(expect.id.val, actual.id.val); |
| 139 | try testing.expectEqual(expect.names.short, actual.names.short); | 156 | try testing.expectEqual(expect.names.short, actual.names.short); |
| 140 | try testing.expectEqual(expect.takes_value, actual.takes_value); | 157 | try testing.expectEqual(expect.takes_value, actual.takes_value); |
| 141 | if (expect.names.long) |long| { | 158 | if (expect.names.long) |long| { |
| @@ -147,58 +164,58 @@ fn expectParam(expect: Param(Help), actual: Param(Help)) !void { | |||
| 147 | 164 | ||
| 148 | test "parseParam" { | 165 | test "parseParam" { |
| 149 | try expectParam(Param(Help){ | 166 | try expectParam(Param(Help){ |
| 150 | .id = .{ .msg = "Help text", .value = "value" }, | 167 | .id = .{ .desc = "Help text", .val = "val" }, |
| 151 | .names = .{ .short = 's', .long = "long" }, | 168 | .names = .{ .short = 's', .long = "long" }, |
| 152 | .takes_value = .one, | 169 | .takes_value = .one, |
| 153 | }, try parseParam("-s, --long <value> Help text")); | 170 | }, try parseParam("-s, --long <val> Help text")); |
| 154 | 171 | ||
| 155 | try expectParam(Param(Help){ | 172 | try expectParam(Param(Help){ |
| 156 | .id = .{ .msg = "Help text", .value = "value" }, | 173 | .id = .{ .desc = "Help text", .val = "val" }, |
| 157 | .names = .{ .short = 's', .long = "long" }, | 174 | .names = .{ .short = 's', .long = "long" }, |
| 158 | .takes_value = .many, | 175 | .takes_value = .many, |
| 159 | }, try parseParam("-s, --long <value>... Help text")); | 176 | }, try parseParam("-s, --long <val>... Help text")); |
| 160 | 177 | ||
| 161 | try expectParam(Param(Help){ | 178 | try expectParam(Param(Help){ |
| 162 | .id = .{ .msg = "Help text", .value = "value" }, | 179 | .id = .{ .desc = "Help text", .val = "val" }, |
| 163 | .names = .{ .long = "long" }, | 180 | .names = .{ .long = "long" }, |
| 164 | .takes_value = .one, | 181 | .takes_value = .one, |
| 165 | }, try parseParam("--long <value> Help text")); | 182 | }, try parseParam("--long <val> Help text")); |
| 166 | 183 | ||
| 167 | try expectParam(Param(Help){ | 184 | try expectParam(Param(Help){ |
| 168 | .id = .{ .msg = "Help text", .value = "value" }, | 185 | .id = .{ .desc = "Help text", .val = "val" }, |
| 169 | .names = .{ .short = 's' }, | 186 | .names = .{ .short = 's' }, |
| 170 | .takes_value = .one, | 187 | .takes_value = .one, |
| 171 | }, try parseParam("-s <value> Help text")); | 188 | }, try parseParam("-s <val> Help text")); |
| 172 | 189 | ||
| 173 | try expectParam(Param(Help){ | 190 | try expectParam(Param(Help){ |
| 174 | .id = .{ .msg = "Help text" }, | 191 | .id = .{ .desc = "Help text" }, |
| 175 | .names = .{ .short = 's', .long = "long" }, | 192 | .names = .{ .short = 's', .long = "long" }, |
| 176 | }, try parseParam("-s, --long Help text")); | 193 | }, try parseParam("-s, --long Help text")); |
| 177 | 194 | ||
| 178 | try expectParam(Param(Help){ | 195 | try expectParam(Param(Help){ |
| 179 | .id = .{ .msg = "Help text" }, | 196 | .id = .{ .desc = "Help text" }, |
| 180 | .names = .{ .short = 's' }, | 197 | .names = .{ .short = 's' }, |
| 181 | }, try parseParam("-s Help text")); | 198 | }, try parseParam("-s Help text")); |
| 182 | 199 | ||
| 183 | try expectParam(Param(Help){ | 200 | try expectParam(Param(Help){ |
| 184 | .id = .{ .msg = "Help text" }, | 201 | .id = .{ .desc = "Help text" }, |
| 185 | .names = .{ .long = "long" }, | 202 | .names = .{ .long = "long" }, |
| 186 | }, try parseParam("--long Help text")); | 203 | }, try parseParam("--long Help text")); |
| 187 | 204 | ||
| 188 | try expectParam(Param(Help){ | 205 | try expectParam(Param(Help){ |
| 189 | .id = .{ .msg = "Help text", .value = "A | B" }, | 206 | .id = .{ .desc = "Help text", .val = "A | B" }, |
| 190 | .names = .{ .long = "long" }, | 207 | .names = .{ .long = "long" }, |
| 191 | .takes_value = .one, | 208 | .takes_value = .one, |
| 192 | }, try parseParam("--long <A | B> Help text")); | 209 | }, try parseParam("--long <A | B> Help text")); |
| 193 | 210 | ||
| 194 | try expectParam(Param(Help){ | 211 | try expectParam(Param(Help){ |
| 195 | .id = .{ .msg = "Help text", .value = "A" }, | 212 | .id = .{ .desc = "Help text", .val = "A" }, |
| 196 | .names = .{}, | 213 | .names = .{}, |
| 197 | .takes_value = .one, | 214 | .takes_value = .one, |
| 198 | }, try parseParam("<A> Help text")); | 215 | }, try parseParam("<A> Help text")); |
| 199 | 216 | ||
| 200 | try expectParam(Param(Help){ | 217 | try expectParam(Param(Help){ |
| 201 | .id = .{ .msg = "Help text", .value = "A" }, | 218 | .id = .{ .desc = "Help text", .val = "A" }, |
| 202 | .names = .{}, | 219 | .names = .{}, |
| 203 | .takes_value = .many, | 220 | .takes_value = .many, |
| 204 | }, try parseParam("<A>... Help text")); | 221 | }, try parseParam("<A>... Help text")); |
| @@ -206,7 +223,7 @@ test "parseParam" { | |||
| 206 | try testing.expectError(error.TrailingComma, parseParam("--long, Help")); | 223 | try testing.expectError(error.TrailingComma, parseParam("--long, Help")); |
| 207 | try testing.expectError(error.TrailingComma, parseParam("-s, Help")); | 224 | try testing.expectError(error.TrailingComma, parseParam("-s, Help")); |
| 208 | try testing.expectError(error.InvalidShortParam, parseParam("-ss Help")); | 225 | try testing.expectError(error.InvalidShortParam, parseParam("-ss Help")); |
| 209 | try testing.expectError(error.InvalidShortParam, parseParam("-ss <value> Help")); | 226 | try testing.expectError(error.InvalidShortParam, parseParam("-ss <val> Help")); |
| 210 | try testing.expectError(error.InvalidShortParam, parseParam("- Help")); | 227 | try testing.expectError(error.InvalidShortParam, parseParam("- Help")); |
| 211 | } | 228 | } |
| 212 | 229 | ||
| @@ -230,15 +247,15 @@ pub const Diagnostic = struct { | |||
| 230 | Arg{ .prefix = "", .name = diag.arg }; | 247 | Arg{ .prefix = "", .name = diag.arg }; |
| 231 | 248 | ||
| 232 | switch (err) { | 249 | switch (err) { |
| 233 | error.DoesntTakeValue => try stream.print( | 250 | streaming.Error.DoesntTakeValue => try stream.print( |
| 234 | "The argument '{s}{s}' does not take a value\n", | 251 | "The argument '{s}{s}' does not take a value\n", |
| 235 | .{ a.prefix, a.name }, | 252 | .{ a.prefix, a.name }, |
| 236 | ), | 253 | ), |
| 237 | error.MissingValue => try stream.print( | 254 | streaming.Error.MissingValue => try stream.print( |
| 238 | "The argument '{s}{s}' requires a value but none was supplied\n", | 255 | "The argument '{s}{s}' requires a value but none was supplied\n", |
| 239 | .{ a.prefix, a.name }, | 256 | .{ a.prefix, a.name }, |
| 240 | ), | 257 | ), |
| 241 | error.InvalidArgument => try stream.print( | 258 | streaming.Error.InvalidArgument => try stream.print( |
| 242 | "Invalid argument '{s}{s}'\n", | 259 | "Invalid argument '{s}{s}'\n", |
| 243 | .{ a.prefix, a.name }, | 260 | .{ a.prefix, a.name }, |
| 244 | ), | 261 | ), |
| @@ -303,34 +320,6 @@ test "Diagnostic.report" { | |||
| 303 | ); | 320 | ); |
| 304 | } | 321 | } |
| 305 | 322 | ||
| 306 | pub fn Args(comptime Id: type, comptime params: []const Param(Id)) type { | ||
| 307 | return struct { | ||
| 308 | arena: std.heap.ArenaAllocator, | ||
| 309 | clap: ComptimeClap(Id, params), | ||
| 310 | exe_arg: ?[]const u8, | ||
| 311 | |||
| 312 | pub fn deinit(a: *@This()) void { | ||
| 313 | a.arena.deinit(); | ||
| 314 | } | ||
| 315 | |||
| 316 | pub fn flag(a: @This(), comptime name: []const u8) bool { | ||
| 317 | return a.clap.flag(name); | ||
| 318 | } | ||
| 319 | |||
| 320 | pub fn option(a: @This(), comptime name: []const u8) ?[]const u8 { | ||
| 321 | return a.clap.option(name); | ||
| 322 | } | ||
| 323 | |||
| 324 | pub fn options(a: @This(), comptime name: []const u8) []const []const u8 { | ||
| 325 | return a.clap.options(name); | ||
| 326 | } | ||
| 327 | |||
| 328 | pub fn positionals(a: @This()) []const []const u8 { | ||
| 329 | return a.clap.positionals(); | ||
| 330 | } | ||
| 331 | }; | ||
| 332 | } | ||
| 333 | |||
| 334 | /// Options that can be set to customize the behavior of parsing. | 323 | /// Options that can be set to customize the behavior of parsing. |
| 335 | pub const ParseOptions = struct { | 324 | pub const ParseOptions = struct { |
| 336 | /// The allocator used for all memory allocations. Defaults to the `heap.page_allocator`. | 325 | /// The allocator used for all memory allocations. Defaults to the `heap.page_allocator`. |
| @@ -346,59 +335,348 @@ pub const ParseOptions = struct { | |||
| 346 | pub fn parse( | 335 | pub fn parse( |
| 347 | comptime Id: type, | 336 | comptime Id: type, |
| 348 | comptime params: []const Param(Id), | 337 | comptime params: []const Param(Id), |
| 338 | comptime value_parsers: anytype, | ||
| 349 | opt: ParseOptions, | 339 | opt: ParseOptions, |
| 350 | ) !Args(Id, params) { | 340 | ) !Result(Id, params, value_parsers) { |
| 351 | var arena = heap.ArenaAllocator.init(opt.allocator); | 341 | var arena = heap.ArenaAllocator.init(opt.allocator); |
| 352 | errdefer arena.deinit(); | 342 | errdefer arena.deinit(); |
| 353 | 343 | ||
| 354 | var iter = try process.ArgIterator.initWithAllocator(arena.allocator()); | 344 | var iter = try process.ArgIterator.initWithAllocator(arena.allocator()); |
| 355 | const exe_arg = iter.next(); | 345 | const exe_arg = iter.next(); |
| 356 | 346 | ||
| 357 | const clap = try parseEx(Id, params, &iter, .{ | 347 | const result = try parseEx(Id, params, value_parsers, &iter, .{ |
| 358 | // Let's reuse the arena from the `OSIterator` since we already have it. | 348 | // Let's reuse the arena from the `OSIterator` since we already have it. |
| 359 | .allocator = arena.allocator(), | 349 | .allocator = arena.allocator(), |
| 360 | .diagnostic = opt.diagnostic, | 350 | .diagnostic = opt.diagnostic, |
| 361 | }); | 351 | }); |
| 362 | 352 | ||
| 363 | return Args(Id, params){ | 353 | return Result(Id, params, value_parsers){ |
| 354 | .args = result.args, | ||
| 355 | .positionals = result.positionals, | ||
| 364 | .exe_arg = exe_arg, | 356 | .exe_arg = exe_arg, |
| 365 | .arena = arena, | 357 | .arena = arena, |
| 366 | .clap = clap, | ||
| 367 | }; | 358 | }; |
| 368 | } | 359 | } |
| 369 | 360 | ||
| 370 | /// Parses the command line arguments passed into the program based on an | 361 | pub fn Result( |
| 371 | /// array of `Param`s. | 362 | comptime Id: type, |
| 363 | comptime params: []const Param(Id), | ||
| 364 | comptime value_parsers: anytype, | ||
| 365 | ) type { | ||
| 366 | return struct { | ||
| 367 | args: Arguments(Id, params, value_parsers, .slice), | ||
| 368 | positionals: []const FindPositionalType(Id, params, value_parsers), | ||
| 369 | exe_arg: ?[]const u8, | ||
| 370 | arena: std.heap.ArenaAllocator, | ||
| 371 | |||
| 372 | pub fn deinit(result: @This()) void { | ||
| 373 | result.arena.deinit(); | ||
| 374 | } | ||
| 375 | }; | ||
| 376 | } | ||
| 377 | |||
| 378 | /// Parses the command line arguments passed into the program based on an array of parameters. | ||
| 379 | /// | ||
| 380 | /// The result will contain an `args` field which contains all the non positional arguments passed | ||
| 381 | /// in. There is a field in `args` for each parameter. The name of that field will be the result | ||
| 382 | /// of this expression: | ||
| 383 | /// ``` | ||
| 384 | /// param.names.longest().name` | ||
| 385 | /// ``` | ||
| 386 | /// | ||
| 387 | /// The fields can have types other that `[]const u8` and this is based on what `value_parsers` | ||
| 388 | /// you provide. The parser to use for each parameter is determined by the following expression: | ||
| 389 | /// ``` | ||
| 390 | /// @field(value_parsers, param.id.value()) | ||
| 391 | /// ``` | ||
| 392 | /// | ||
| 393 | /// Where `value` is a function that returns the name of the value this parameter takes. A parser | ||
| 394 | /// is simple a function with the signature: | ||
| 395 | /// ``` | ||
| 396 | /// fn ([]const u8) Error!T | ||
| 397 | /// ``` | ||
| 398 | /// | ||
| 399 | /// `T` can be any type and `Error` can be any error. You can pass `clap.parsers.default` if you | ||
| 400 | /// just wonna get something up and running. | ||
| 401 | /// | ||
| 402 | /// Caller ownes the result and should free it by calling `result.deinit()` | ||
| 372 | pub fn parseEx( | 403 | pub fn parseEx( |
| 373 | comptime Id: type, | 404 | comptime Id: type, |
| 374 | comptime params: []const Param(Id), | 405 | comptime params: []const Param(Id), |
| 406 | comptime value_parsers: anytype, | ||
| 375 | iter: anytype, | 407 | iter: anytype, |
| 376 | opt: ParseOptions, | 408 | opt: ParseOptions, |
| 377 | ) !ComptimeClap(Id, params) { | 409 | ) !ResultEx(Id, params, value_parsers) { |
| 378 | const Clap = ComptimeClap(Id, params); | 410 | const allocator = opt.allocator; |
| 379 | return try Clap.parse(iter, opt); | 411 | var positionals = std.ArrayList( |
| 412 | FindPositionalType(Id, params, value_parsers), | ||
| 413 | ).init(allocator); | ||
| 414 | |||
| 415 | var arguments = Arguments(Id, params, value_parsers, .list){}; | ||
| 416 | errdefer deinitArgs(Id, params, value_parsers, .list, allocator, &arguments); | ||
| 417 | |||
| 418 | var stream = streaming.Clap(Id, @typeInfo(@TypeOf(iter)).Pointer.child){ | ||
| 419 | .params = params, | ||
| 420 | .iter = iter, | ||
| 421 | .diagnostic = opt.diagnostic, | ||
| 422 | }; | ||
| 423 | while (try stream.next()) |arg| { | ||
| 424 | inline for (params) |*param| { | ||
| 425 | if (param == arg.param) { | ||
| 426 | const parser = comptime switch (param.takes_value) { | ||
| 427 | .none => undefined, | ||
| 428 | .one, .many => @field(value_parsers, param.id.value()), | ||
| 429 | }; | ||
| 430 | |||
| 431 | // TODO: Update opt.diagnostics when `parser` fails. This is blocked by compiler | ||
| 432 | // bugs that causes an infinit loop. | ||
| 433 | const longest = comptime param.names.longest(); | ||
| 434 | switch (longest.kind) { | ||
| 435 | .short, .long => switch (param.takes_value) { | ||
| 436 | .none => @field(arguments, longest.name) = true, | ||
| 437 | .one => @field(arguments, longest.name) = try parser(arg.value.?), | ||
| 438 | .many => { | ||
| 439 | const value = try parser(arg.value.?); | ||
| 440 | try @field(arguments, longest.name).append(allocator, value); | ||
| 441 | }, | ||
| 442 | }, | ||
| 443 | .positinal => try positionals.append(try parser(arg.value.?)), | ||
| 444 | } | ||
| 445 | } | ||
| 446 | } | ||
| 447 | } | ||
| 448 | |||
| 449 | var result_args = Arguments(Id, params, value_parsers, .slice){}; | ||
| 450 | inline for (@typeInfo(@TypeOf(arguments)).Struct.fields) |field| { | ||
| 451 | if (@typeInfo(field.field_type) == .Struct and | ||
| 452 | @hasDecl(field.field_type, "toOwnedSlice")) | ||
| 453 | { | ||
| 454 | const slice = @field(arguments, field.name).toOwnedSlice(allocator); | ||
| 455 | @field(result_args, field.name) = slice; | ||
| 456 | } else { | ||
| 457 | @field(result_args, field.name) = @field(arguments, field.name); | ||
| 458 | } | ||
| 459 | } | ||
| 460 | |||
| 461 | return ResultEx(Id, params, value_parsers){ | ||
| 462 | .args = result_args, | ||
| 463 | .positionals = positionals.toOwnedSlice(), | ||
| 464 | .allocator = allocator, | ||
| 465 | }; | ||
| 466 | } | ||
| 467 | |||
| 468 | pub fn ResultEx( | ||
| 469 | comptime Id: type, | ||
| 470 | comptime params: []const Param(Id), | ||
| 471 | comptime value_parsers: anytype, | ||
| 472 | ) type { | ||
| 473 | return struct { | ||
| 474 | args: Arguments(Id, params, value_parsers, .slice), | ||
| 475 | positionals: []const FindPositionalType(Id, params, value_parsers), | ||
| 476 | allocator: mem.Allocator, | ||
| 477 | |||
| 478 | pub fn deinit(result: *@This()) void { | ||
| 479 | deinitArgs(Id, params, value_parsers, .slice, result.allocator, &result.args); | ||
| 480 | result.allocator.free(result.positionals); | ||
| 481 | } | ||
| 482 | }; | ||
| 483 | } | ||
| 484 | |||
| 485 | fn FindPositionalType( | ||
| 486 | comptime Id: type, | ||
| 487 | comptime params: []const Param(Id), | ||
| 488 | comptime value_parsers: anytype, | ||
| 489 | ) type { | ||
| 490 | for (params) |param| { | ||
| 491 | const longest = param.names.longest(); | ||
| 492 | if (longest.kind == .positinal) | ||
| 493 | return ParamType(Id, param, value_parsers); | ||
| 494 | } | ||
| 495 | |||
| 496 | return []const u8; | ||
| 497 | } | ||
| 498 | |||
| 499 | fn ParamType( | ||
| 500 | comptime Id: type, | ||
| 501 | comptime param: Param(Id), | ||
| 502 | comptime value_parsers: anytype, | ||
| 503 | ) type { | ||
| 504 | const parser = switch (param.takes_value) { | ||
| 505 | .none => parsers.string, | ||
| 506 | .one, .many => @field(value_parsers, param.id.value()), | ||
| 507 | }; | ||
| 508 | return parsers.Result(@TypeOf(parser)); | ||
| 509 | } | ||
| 510 | |||
| 511 | fn deinitArgs( | ||
| 512 | comptime Id: type, | ||
| 513 | comptime params: []const Param(Id), | ||
| 514 | comptime value_parsers: anytype, | ||
| 515 | comptime multi_arg_kind: MultiArgKind, | ||
| 516 | allocator: mem.Allocator, | ||
| 517 | arguments: *Arguments(Id, params, value_parsers, multi_arg_kind), | ||
| 518 | ) void { | ||
| 519 | inline for (params) |param| { | ||
| 520 | const longest = comptime param.names.longest(); | ||
| 521 | if (longest.kind == .positinal) | ||
| 522 | continue; | ||
| 523 | if (param.takes_value != .many) | ||
| 524 | continue; | ||
| 525 | |||
| 526 | switch (multi_arg_kind) { | ||
| 527 | .slice => allocator.free(@field(arguments, longest.name)), | ||
| 528 | .list => @field(arguments, longest.name).deinit(allocator), | ||
| 529 | } | ||
| 530 | } | ||
| 380 | } | 531 | } |
| 381 | 532 | ||
| 533 | const MultiArgKind = enum { slice, list }; | ||
| 534 | |||
| 535 | fn Arguments( | ||
| 536 | comptime Id: type, | ||
| 537 | comptime params: []const Param(Id), | ||
| 538 | comptime value_parsers: anytype, | ||
| 539 | comptime multi_arg_kind: MultiArgKind, | ||
| 540 | ) type { | ||
| 541 | var fields: [params.len]builtin.TypeInfo.StructField = undefined; | ||
| 542 | |||
| 543 | var i: usize = 0; | ||
| 544 | for (params) |param| { | ||
| 545 | const longest = param.names.longest(); | ||
| 546 | if (longest.kind == .positinal) | ||
| 547 | continue; | ||
| 548 | |||
| 549 | const T = ParamType(Id, param, value_parsers); | ||
| 550 | const FieldType = switch (param.takes_value) { | ||
| 551 | .none => bool, | ||
| 552 | .one => ?T, | ||
| 553 | .many => switch (multi_arg_kind) { | ||
| 554 | .slice => []const T, | ||
| 555 | .list => std.ArrayListUnmanaged(T), | ||
| 556 | }, | ||
| 557 | }; | ||
| 558 | fields[i] = .{ | ||
| 559 | .name = longest.name, | ||
| 560 | .field_type = FieldType, | ||
| 561 | .default_value = switch (param.takes_value) { | ||
| 562 | .none => &false, | ||
| 563 | .one => &@as(?T, null), | ||
| 564 | .many => switch (multi_arg_kind) { | ||
| 565 | .slice => &@as([]const T, &[_]T{}), | ||
| 566 | .list => &std.ArrayListUnmanaged(T){}, | ||
| 567 | }, | ||
| 568 | }, | ||
| 569 | .is_comptime = false, | ||
| 570 | .alignment = @alignOf(FieldType), | ||
| 571 | }; | ||
| 572 | i += 1; | ||
| 573 | } | ||
| 574 | |||
| 575 | return @Type(.{ .Struct = .{ | ||
| 576 | .layout = .Auto, | ||
| 577 | .fields = fields[0..i], | ||
| 578 | .decls = &.{}, | ||
| 579 | .is_tuple = false, | ||
| 580 | } }); | ||
| 581 | } | ||
| 582 | |||
| 583 | test "" { | ||
| 584 | const params = comptime &.{ | ||
| 585 | parseParam("-a, --aa") catch unreachable, | ||
| 586 | parseParam("-b, --bb") catch unreachable, | ||
| 587 | parseParam("-c, --cc <str>") catch unreachable, | ||
| 588 | parseParam("-d, --dd <usize>...") catch unreachable, | ||
| 589 | parseParam("<str>") catch unreachable, | ||
| 590 | }; | ||
| 591 | |||
| 592 | var iter = args.SliceIterator{ | ||
| 593 | .args = &.{ "-a", "-c", "0", "something", "-d", "1", "--dd", "2" }, | ||
| 594 | }; | ||
| 595 | var res = try parseEx(Help, params, parsers.default, &iter, .{ | ||
| 596 | .allocator = testing.allocator, | ||
| 597 | }); | ||
| 598 | defer res.deinit(); | ||
| 599 | |||
| 600 | try testing.expect(res.args.aa); | ||
| 601 | try testing.expect(!res.args.bb); | ||
| 602 | try testing.expectEqualStrings("0", res.args.cc.?); | ||
| 603 | try testing.expectEqual(@as(usize, 1), res.positionals.len); | ||
| 604 | try testing.expectEqualStrings("something", res.positionals[0]); | ||
| 605 | try testing.expectEqualSlices(usize, &.{ 1, 2 }, res.args.dd); | ||
| 606 | } | ||
| 607 | |||
| 608 | test "empty" { | ||
| 609 | var iter = args.SliceIterator{ .args = &.{} }; | ||
| 610 | var res = try parseEx(u8, &.{}, parsers.default, &iter, .{ .allocator = testing.allocator }); | ||
| 611 | defer res.deinit(); | ||
| 612 | } | ||
| 613 | |||
| 614 | fn testErr( | ||
| 615 | comptime params: []const Param(Help), | ||
| 616 | args_strings: []const []const u8, | ||
| 617 | expected: []const u8, | ||
| 618 | ) !void { | ||
| 619 | var diag = Diagnostic{}; | ||
| 620 | var iter = args.SliceIterator{ .args = args_strings }; | ||
| 621 | _ = parseEx(Help, params, parsers.default, &iter, .{ | ||
| 622 | .allocator = testing.allocator, | ||
| 623 | .diagnostic = &diag, | ||
| 624 | }) catch |err| { | ||
| 625 | var buf: [1024]u8 = undefined; | ||
| 626 | var fbs = io.fixedBufferStream(&buf); | ||
| 627 | diag.report(fbs.writer(), err) catch return error.TestFailed; | ||
| 628 | try testing.expectEqualStrings(expected, fbs.getWritten()); | ||
| 629 | return; | ||
| 630 | }; | ||
| 631 | |||
| 632 | try testing.expect(false); | ||
| 633 | } | ||
| 634 | |||
| 635 | test "errors" { | ||
| 636 | const params = comptime [_]Param(Help){ | ||
| 637 | parseParam("-a, --aa") catch unreachable, | ||
| 638 | parseParam("-c, --cc <str>") catch unreachable, | ||
| 639 | }; | ||
| 640 | |||
| 641 | try testErr(¶ms, &.{"q"}, "Invalid argument 'q'\n"); | ||
| 642 | try testErr(¶ms, &.{"-q"}, "Invalid argument '-q'\n"); | ||
| 643 | try testErr(¶ms, &.{"--q"}, "Invalid argument '--q'\n"); | ||
| 644 | try testErr(¶ms, &.{"--q=1"}, "Invalid argument '--q'\n"); | ||
| 645 | try testErr(¶ms, &.{"-a=1"}, "The argument '-a' does not take a value\n"); | ||
| 646 | try testErr(¶ms, &.{"--aa=1"}, "The argument '--aa' does not take a value\n"); | ||
| 647 | try testErr(¶ms, &.{"-c"}, "The argument '-c' requires a value but none was supplied\n"); | ||
| 648 | try testErr( | ||
| 649 | ¶ms, | ||
| 650 | &.{"--cc"}, | ||
| 651 | "The argument '--cc' requires a value but none was supplied\n", | ||
| 652 | ); | ||
| 653 | } | ||
| 654 | |||
| 655 | pub const Help = struct { | ||
| 656 | desc: []const u8 = "", | ||
| 657 | val: []const u8 = "", | ||
| 658 | |||
| 659 | pub fn description(h: Help) []const u8 { | ||
| 660 | return h.desc; | ||
| 661 | } | ||
| 662 | |||
| 663 | pub fn value(h: Help) []const u8 { | ||
| 664 | return h.val; | ||
| 665 | } | ||
| 666 | }; | ||
| 667 | |||
| 382 | /// Will print a help message in the following format: | 668 | /// Will print a help message in the following format: |
| 383 | /// -s, --long <valueText> helpText | 669 | /// -s, --long <valueText> helpText |
| 384 | /// -s, helpText | 670 | /// -s, helpText |
| 385 | /// -s <valueText> helpText | 671 | /// -s <valueText> helpText |
| 386 | /// --long helpText | 672 | /// --long helpText |
| 387 | /// --long <valueText> helpText | 673 | /// --long <valueText> helpText |
| 388 | pub fn helpFull( | 674 | pub fn help(stream: anytype, comptime Id: type, params: []const Param(Id)) !void { |
| 389 | stream: anytype, | ||
| 390 | comptime Id: type, | ||
| 391 | params: []const Param(Id), | ||
| 392 | comptime Error: type, | ||
| 393 | context: anytype, | ||
| 394 | helpText: fn (@TypeOf(context), Param(Id)) Error![]const u8, | ||
| 395 | valueText: fn (@TypeOf(context), Param(Id)) Error![]const u8, | ||
| 396 | ) !void { | ||
| 397 | const max_spacing = blk: { | 675 | const max_spacing = blk: { |
| 398 | var res: usize = 0; | 676 | var res: usize = 0; |
| 399 | for (params) |param| { | 677 | for (params) |param| { |
| 400 | var cs = io.countingWriter(io.null_writer); | 678 | var cs = io.countingWriter(io.null_writer); |
| 401 | try printParam(cs.writer(), Id, param, Error, context, valueText); | 679 | try printParam(cs.writer(), Id, param); |
| 402 | if (res < cs.bytes_written) | 680 | if (res < cs.bytes_written) |
| 403 | res = @intCast(usize, cs.bytes_written); | 681 | res = @intCast(usize, cs.bytes_written); |
| 404 | } | 682 | } |
| @@ -412,13 +690,13 @@ pub fn helpFull( | |||
| 412 | 690 | ||
| 413 | var cs = io.countingWriter(stream); | 691 | var cs = io.countingWriter(stream); |
| 414 | try stream.writeAll("\t"); | 692 | try stream.writeAll("\t"); |
| 415 | try printParam(cs.writer(), Id, param, Error, context, valueText); | 693 | try printParam(cs.writer(), Id, param); |
| 416 | try stream.writeByteNTimes(' ', max_spacing - @intCast(usize, cs.bytes_written)); | 694 | try stream.writeByteNTimes(' ', max_spacing - @intCast(usize, cs.bytes_written)); |
| 417 | 695 | ||
| 418 | const help_text = try helpText(context, param); | 696 | const description = param.id.description(); |
| 419 | var help_text_line_it = mem.split(u8, help_text, "\n"); | 697 | var it = mem.split(u8, description, "\n"); |
| 420 | var indent_line = false; | 698 | var indent_line = false; |
| 421 | while (help_text_line_it.next()) |line| : (indent_line = true) { | 699 | while (it.next()) |line| : (indent_line = true) { |
| 422 | if (indent_line) { | 700 | if (indent_line) { |
| 423 | try stream.writeAll("\t"); | 701 | try stream.writeAll("\t"); |
| 424 | try stream.writeByteNTimes(' ', max_spacing); | 702 | try stream.writeByteNTimes(' ', max_spacing); |
| @@ -434,9 +712,6 @@ fn printParam( | |||
| 434 | stream: anytype, | 712 | stream: anytype, |
| 435 | comptime Id: type, | 713 | comptime Id: type, |
| 436 | param: Param(Id), | 714 | param: Param(Id), |
| 437 | comptime Error: type, | ||
| 438 | context: anytype, | ||
| 439 | valueText: fn (@TypeOf(context), Param(Id)) Error![]const u8, | ||
| 440 | ) !void { | 715 | ) !void { |
| 441 | if (param.names.short) |s| { | 716 | if (param.names.short) |s| { |
| 442 | try stream.writeAll(&[_]u8{ '-', s }); | 717 | try stream.writeAll(&[_]u8{ '-', s }); |
| @@ -458,66 +733,12 @@ fn printParam( | |||
| 458 | return; | 733 | return; |
| 459 | 734 | ||
| 460 | try stream.writeAll(" <"); | 735 | try stream.writeAll(" <"); |
| 461 | try stream.writeAll(try valueText(context, param)); | 736 | try stream.writeAll(param.id.value()); |
| 462 | try stream.writeAll(">"); | 737 | try stream.writeAll(">"); |
| 463 | if (param.takes_value == .many) | 738 | if (param.takes_value == .many) |
| 464 | try stream.writeAll("..."); | 739 | try stream.writeAll("..."); |
| 465 | } | 740 | } |
| 466 | 741 | ||
| 467 | /// A wrapper around helpFull for simple helpText and valueText functions that | ||
| 468 | /// cant return an error or take a context. | ||
| 469 | pub fn helpEx( | ||
| 470 | stream: anytype, | ||
| 471 | comptime Id: type, | ||
| 472 | params: []const Param(Id), | ||
| 473 | helpText: fn (Param(Id)) []const u8, | ||
| 474 | valueText: fn (Param(Id)) []const u8, | ||
| 475 | ) !void { | ||
| 476 | const Context = struct { | ||
| 477 | helpText: fn (Param(Id)) []const u8, | ||
| 478 | valueText: fn (Param(Id)) []const u8, | ||
| 479 | |||
| 480 | pub fn help(c: @This(), p: Param(Id)) error{}![]const u8 { | ||
| 481 | return c.helpText(p); | ||
| 482 | } | ||
| 483 | |||
| 484 | pub fn value(c: @This(), p: Param(Id)) error{}![]const u8 { | ||
| 485 | return c.valueText(p); | ||
| 486 | } | ||
| 487 | }; | ||
| 488 | |||
| 489 | return helpFull( | ||
| 490 | stream, | ||
| 491 | Id, | ||
| 492 | params, | ||
| 493 | error{}, | ||
| 494 | Context{ | ||
| 495 | .helpText = helpText, | ||
| 496 | .valueText = valueText, | ||
| 497 | }, | ||
| 498 | Context.help, | ||
| 499 | Context.value, | ||
| 500 | ); | ||
| 501 | } | ||
| 502 | |||
| 503 | pub const Help = struct { | ||
| 504 | msg: []const u8 = "", | ||
| 505 | value: []const u8 = "", | ||
| 506 | }; | ||
| 507 | |||
| 508 | /// A wrapper around helpEx that takes a Param(Help). | ||
| 509 | pub fn help(stream: anytype, params: []const Param(Help)) !void { | ||
| 510 | try helpEx(stream, Help, params, getHelpSimple, getValueSimple); | ||
| 511 | } | ||
| 512 | |||
| 513 | fn getHelpSimple(param: Param(Help)) []const u8 { | ||
| 514 | return param.id.msg; | ||
| 515 | } | ||
| 516 | |||
| 517 | fn getValueSimple(param: Param(Help)) []const u8 { | ||
| 518 | return param.id.value; | ||
| 519 | } | ||
| 520 | |||
| 521 | test "clap.help" { | 742 | test "clap.help" { |
| 522 | var buf: [1024]u8 = undefined; | 743 | var buf: [1024]u8 = undefined; |
| 523 | var slice_stream = io.fixedBufferStream(&buf); | 744 | var slice_stream = io.fixedBufferStream(&buf); |
| @@ -525,6 +746,7 @@ test "clap.help" { | |||
| 525 | @setEvalBranchQuota(10000); | 746 | @setEvalBranchQuota(10000); |
| 526 | try help( | 747 | try help( |
| 527 | slice_stream.writer(), | 748 | slice_stream.writer(), |
| 749 | Help, | ||
| 528 | comptime &.{ | 750 | comptime &.{ |
| 529 | parseParam("-a Short flag.") catch unreachable, | 751 | parseParam("-a Short flag.") catch unreachable, |
| 530 | parseParam("-b <V1> Short option.") catch unreachable, | 752 | parseParam("-b <V1> Short option.") catch unreachable, |
| @@ -556,18 +778,11 @@ test "clap.help" { | |||
| 556 | } | 778 | } |
| 557 | 779 | ||
| 558 | /// Will print a usage message in the following format: | 780 | /// Will print a usage message in the following format: |
| 559 | /// [-abc] [--longa] [-d <valueText>] [--longb <valueText>] <valueText> | 781 | /// [-abc] [--longa] [-d <T>] [--longb <T>] <T> |
| 560 | /// | 782 | /// |
| 561 | /// First all none value taking parameters, which have a short name are | 783 | /// First all none value taking parameters, which have a short name are printed, then non |
| 562 | /// printed, then non positional parameters and finally the positinal. | 784 | /// positional parameters and finally the positinal. |
| 563 | pub fn usageFull( | 785 | pub fn usage(stream: anytype, comptime Id: type, params: []const Param(Id)) !void { |
| 564 | stream: anytype, | ||
| 565 | comptime Id: type, | ||
| 566 | params: []const Param(Id), | ||
| 567 | comptime Error: type, | ||
| 568 | context: anytype, | ||
| 569 | valueText: fn (@TypeOf(context), Param(Id)) Error![]const u8, | ||
| 570 | ) !void { | ||
| 571 | var cos = io.countingWriter(stream); | 786 | var cos = io.countingWriter(stream); |
| 572 | const cs = cos.writer(); | 787 | const cs = cos.writer(); |
| 573 | for (params) |param| { | 788 | for (params) |param| { |
| @@ -607,7 +822,7 @@ pub fn usageFull( | |||
| 607 | try cs.writeAll(name); | 822 | try cs.writeAll(name); |
| 608 | if (param.takes_value != .none) { | 823 | if (param.takes_value != .none) { |
| 609 | try cs.writeAll(" <"); | 824 | try cs.writeAll(" <"); |
| 610 | try cs.writeAll(try valueText(context, param)); | 825 | try cs.writeAll(param.id.value()); |
| 611 | try cs.writeAll(">"); | 826 | try cs.writeAll(">"); |
| 612 | if (param.takes_value == .many) | 827 | if (param.takes_value == .many) |
| 613 | try cs.writeAll("..."); | 828 | try cs.writeAll("..."); |
| @@ -621,46 +836,15 @@ pub fn usageFull( | |||
| 621 | try cs.writeAll(" "); | 836 | try cs.writeAll(" "); |
| 622 | 837 | ||
| 623 | try cs.writeAll("<"); | 838 | try cs.writeAll("<"); |
| 624 | try cs.writeAll(try valueText(context, p)); | 839 | try cs.writeAll(p.id.value()); |
| 625 | try cs.writeAll(">"); | 840 | try cs.writeAll(">"); |
| 626 | } | 841 | } |
| 627 | } | 842 | } |
| 628 | 843 | ||
| 629 | /// A wrapper around usageFull for a simple valueText functions that | ||
| 630 | /// cant return an error or take a context. | ||
| 631 | pub fn usageEx( | ||
| 632 | stream: anytype, | ||
| 633 | comptime Id: type, | ||
| 634 | params: []const Param(Id), | ||
| 635 | valueText: fn (Param(Id)) []const u8, | ||
| 636 | ) !void { | ||
| 637 | const Context = struct { | ||
| 638 | valueText: fn (Param(Id)) []const u8, | ||
| 639 | |||
| 640 | pub fn value(c: @This(), p: Param(Id)) error{}![]const u8 { | ||
| 641 | return c.valueText(p); | ||
| 642 | } | ||
| 643 | }; | ||
| 644 | |||
| 645 | return usageFull( | ||
| 646 | stream, | ||
| 647 | Id, | ||
| 648 | params, | ||
| 649 | error{}, | ||
| 650 | Context{ .valueText = valueText }, | ||
| 651 | Context.value, | ||
| 652 | ); | ||
| 653 | } | ||
| 654 | |||
| 655 | /// A wrapper around usageEx that takes a Param(Help). | ||
| 656 | pub fn usage(stream: anytype, params: []const Param(Help)) !void { | ||
| 657 | try usageEx(stream, Help, params, getValueSimple); | ||
| 658 | } | ||
| 659 | |||
| 660 | fn testUsage(expected: []const u8, params: []const Param(Help)) !void { | 844 | fn testUsage(expected: []const u8, params: []const Param(Help)) !void { |
| 661 | var buf: [1024]u8 = undefined; | 845 | var buf: [1024]u8 = undefined; |
| 662 | var fbs = io.fixedBufferStream(&buf); | 846 | var fbs = io.fixedBufferStream(&buf); |
| 663 | try usage(fbs.writer(), params); | 847 | try usage(fbs.writer(), Help, params); |
| 664 | try testing.expectEqualStrings(expected, fbs.getWritten()); | 848 | try testing.expectEqualStrings(expected, fbs.getWritten()); |
| 665 | } | 849 | } |
| 666 | 850 | ||
diff --git a/clap/comptime.zig b/clap/comptime.zig deleted file mode 100644 index b440004..0000000 --- a/clap/comptime.zig +++ /dev/null | |||
| @@ -1,237 +0,0 @@ | |||
| 1 | const clap = @import("../clap.zig"); | ||
| 2 | const std = @import("std"); | ||
| 3 | |||
| 4 | const debug = std.debug; | ||
| 5 | const heap = std.heap; | ||
| 6 | const io = std.io; | ||
| 7 | const mem = std.mem; | ||
| 8 | const testing = std.testing; | ||
| 9 | |||
| 10 | /// Deprecated: Use `parseEx` instead | ||
| 11 | pub fn ComptimeClap( | ||
| 12 | comptime Id: type, | ||
| 13 | comptime params: []const clap.Param(Id), | ||
| 14 | ) type { | ||
| 15 | comptime var flags: usize = 0; | ||
| 16 | comptime var single_options: usize = 0; | ||
| 17 | comptime var multi_options: usize = 0; | ||
| 18 | comptime var converted_params: []const clap.Param(usize) = &.{}; | ||
| 19 | for (params) |param| { | ||
| 20 | var index: usize = 0; | ||
| 21 | if (param.names.long != null or param.names.short != null) { | ||
| 22 | const ptr = switch (param.takes_value) { | ||
| 23 | .none => &flags, | ||
| 24 | .one => &single_options, | ||
| 25 | .many => &multi_options, | ||
| 26 | }; | ||
| 27 | index = ptr.*; | ||
| 28 | ptr.* += 1; | ||
| 29 | } | ||
| 30 | |||
| 31 | converted_params = converted_params ++ [_]clap.Param(usize){.{ | ||
| 32 | .id = index, | ||
| 33 | .names = param.names, | ||
| 34 | .takes_value = param.takes_value, | ||
| 35 | }}; | ||
| 36 | } | ||
| 37 | |||
| 38 | return struct { | ||
| 39 | multi_options: [multi_options][]const []const u8, | ||
| 40 | single_options: [single_options][]const u8, | ||
| 41 | single_options_is_set: std.PackedIntArray(u1, single_options), | ||
| 42 | flags: std.PackedIntArray(u1, flags), | ||
| 43 | pos: []const []const u8, | ||
| 44 | allocator: mem.Allocator, | ||
| 45 | |||
| 46 | pub fn parse(iter: anytype, opt: clap.ParseOptions) !@This() { | ||
| 47 | const allocator = opt.allocator; | ||
| 48 | var multis = [_]std.ArrayList([]const u8){undefined} ** multi_options; | ||
| 49 | for (multis) |*multi| | ||
| 50 | multi.* = std.ArrayList([]const u8).init(allocator); | ||
| 51 | |||
| 52 | var pos = std.ArrayList([]const u8).init(allocator); | ||
| 53 | |||
| 54 | var res = @This(){ | ||
| 55 | .multi_options = .{undefined} ** multi_options, | ||
| 56 | .single_options = .{undefined} ** single_options, | ||
| 57 | .single_options_is_set = std.PackedIntArray(u1, single_options).init( | ||
| 58 | .{0} ** single_options, | ||
| 59 | ), | ||
| 60 | .flags = std.PackedIntArray(u1, flags).init(.{0} ** flags), | ||
| 61 | .pos = undefined, | ||
| 62 | .allocator = allocator, | ||
| 63 | }; | ||
| 64 | |||
| 65 | var stream = clap.StreamingClap(usize, @typeInfo(@TypeOf(iter)).Pointer.child){ | ||
| 66 | .params = converted_params, | ||
| 67 | .iter = iter, | ||
| 68 | .diagnostic = opt.diagnostic, | ||
| 69 | }; | ||
| 70 | while (try stream.next()) |arg| { | ||
| 71 | const param = arg.param; | ||
| 72 | if (param.names.long == null and param.names.short == null) { | ||
| 73 | try pos.append(arg.value.?); | ||
| 74 | } else if (param.takes_value == .one) { | ||
| 75 | debug.assert(res.single_options.len != 0); | ||
| 76 | if (res.single_options.len != 0) { | ||
| 77 | res.single_options[param.id] = arg.value.?; | ||
| 78 | res.single_options_is_set.set(param.id, 1); | ||
| 79 | } | ||
| 80 | } else if (param.takes_value == .many) { | ||
| 81 | debug.assert(multis.len != 0); | ||
| 82 | if (multis.len != 0) | ||
| 83 | try multis[param.id].append(arg.value.?); | ||
| 84 | } else { | ||
| 85 | debug.assert(res.flags.len != 0); | ||
| 86 | if (res.flags.len != 0) | ||
| 87 | res.flags.set(param.id, 1); | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | for (multis) |*multi, i| | ||
| 92 | res.multi_options[i] = multi.toOwnedSlice(); | ||
| 93 | res.pos = pos.toOwnedSlice(); | ||
| 94 | |||
| 95 | return res; | ||
| 96 | } | ||
| 97 | |||
| 98 | pub fn deinit(parser: @This()) void { | ||
| 99 | for (parser.multi_options) |o| | ||
| 100 | parser.allocator.free(o); | ||
| 101 | parser.allocator.free(parser.pos); | ||
| 102 | } | ||
| 103 | |||
| 104 | pub fn flag(parser: @This(), comptime name: []const u8) bool { | ||
| 105 | const param = comptime findParam(name); | ||
| 106 | if (param.takes_value != .none) | ||
| 107 | @compileError(name ++ " is an option and not a flag."); | ||
| 108 | |||
| 109 | return parser.flags.get(param.id) != 0; | ||
| 110 | } | ||
| 111 | |||
| 112 | pub fn option(parser: @This(), comptime name: []const u8) ?[]const u8 { | ||
| 113 | const param = comptime findParam(name); | ||
| 114 | if (param.takes_value == .none) | ||
| 115 | @compileError(name ++ " is a flag and not an option."); | ||
| 116 | if (param.takes_value == .many) | ||
| 117 | @compileError(name ++ " takes many options, not one."); | ||
| 118 | if (parser.single_options_is_set.get(param.id) == 0) | ||
| 119 | return null; | ||
| 120 | return parser.single_options[param.id]; | ||
| 121 | } | ||
| 122 | |||
| 123 | pub fn options(parser: @This(), comptime name: []const u8) []const []const u8 { | ||
| 124 | const param = comptime findParam(name); | ||
| 125 | if (param.takes_value == .none) | ||
| 126 | @compileError(name ++ " is a flag and not an option."); | ||
| 127 | if (param.takes_value == .one) | ||
| 128 | @compileError(name ++ " takes one option, not multiple."); | ||
| 129 | |||
| 130 | return parser.multi_options[param.id]; | ||
| 131 | } | ||
| 132 | |||
| 133 | pub fn positionals(parser: @This()) []const []const u8 { | ||
| 134 | return parser.pos; | ||
| 135 | } | ||
| 136 | |||
| 137 | fn findParam(comptime name: []const u8) clap.Param(usize) { | ||
| 138 | comptime { | ||
| 139 | for (converted_params) |param| { | ||
| 140 | if (param.names.short) |s| { | ||
| 141 | if (mem.eql(u8, name, "-" ++ [_]u8{s})) | ||
| 142 | return param; | ||
| 143 | } | ||
| 144 | if (param.names.long) |l| { | ||
| 145 | if (mem.eql(u8, name, "--" ++ l)) | ||
| 146 | return param; | ||
| 147 | } | ||
| 148 | } | ||
| 149 | |||
| 150 | @compileError(name ++ " is not a parameter."); | ||
| 151 | } | ||
| 152 | } | ||
| 153 | }; | ||
| 154 | } | ||
| 155 | |||
| 156 | test "" { | ||
| 157 | const params = comptime &.{ | ||
| 158 | clap.parseParam("-a, --aa") catch unreachable, | ||
| 159 | clap.parseParam("-b, --bb") catch unreachable, | ||
| 160 | clap.parseParam("-c, --cc <V>") catch unreachable, | ||
| 161 | clap.parseParam("-d, --dd <V>...") catch unreachable, | ||
| 162 | clap.parseParam("<P>") catch unreachable, | ||
| 163 | }; | ||
| 164 | |||
| 165 | var iter = clap.args.SliceIterator{ | ||
| 166 | .args = &.{ | ||
| 167 | "-a", "-c", "0", "something", "-d", "a", "--dd", "b", | ||
| 168 | }, | ||
| 169 | }; | ||
| 170 | var args = try clap.parseEx(clap.Help, params, &iter, .{ .allocator = testing.allocator }); | ||
| 171 | defer args.deinit(); | ||
| 172 | |||
| 173 | try testing.expect(args.flag("-a")); | ||
| 174 | try testing.expect(args.flag("--aa")); | ||
| 175 | try testing.expect(!args.flag("-b")); | ||
| 176 | try testing.expect(!args.flag("--bb")); | ||
| 177 | try testing.expectEqualStrings("0", args.option("-c").?); | ||
| 178 | try testing.expectEqualStrings("0", args.option("--cc").?); | ||
| 179 | try testing.expectEqual(@as(usize, 1), args.positionals().len); | ||
| 180 | try testing.expectEqualStrings("something", args.positionals()[0]); | ||
| 181 | try testing.expectEqualSlices([]const u8, &.{ "a", "b" }, args.options("-d")); | ||
| 182 | try testing.expectEqualSlices([]const u8, &.{ "a", "b" }, args.options("--dd")); | ||
| 183 | } | ||
| 184 | |||
| 185 | test "empty" { | ||
| 186 | var iter = clap.args.SliceIterator{ .args = &.{} }; | ||
| 187 | var args = try clap.parseEx(u8, &.{}, &iter, .{ .allocator = testing.allocator }); | ||
| 188 | defer args.deinit(); | ||
| 189 | } | ||
| 190 | |||
| 191 | fn testErr( | ||
| 192 | comptime params: []const clap.Param(u8), | ||
| 193 | args_strings: []const []const u8, | ||
| 194 | expected: []const u8, | ||
| 195 | ) !void { | ||
| 196 | var diag = clap.Diagnostic{}; | ||
| 197 | var iter = clap.args.SliceIterator{ .args = args_strings }; | ||
| 198 | _ = clap.parseEx(u8, params, &iter, .{ | ||
| 199 | .allocator = testing.allocator, | ||
| 200 | .diagnostic = &diag, | ||
| 201 | }) catch |err| { | ||
| 202 | var buf: [1024]u8 = undefined; | ||
| 203 | var fbs = io.fixedBufferStream(&buf); | ||
| 204 | diag.report(fbs.writer(), err) catch return error.TestFailed; | ||
| 205 | try testing.expectEqualStrings(expected, fbs.getWritten()); | ||
| 206 | return; | ||
| 207 | }; | ||
| 208 | |||
| 209 | try testing.expect(false); | ||
| 210 | } | ||
| 211 | |||
| 212 | test "errors" { | ||
| 213 | const params = [_]clap.Param(u8){ | ||
| 214 | .{ | ||
| 215 | .id = 0, | ||
| 216 | .names = .{ .short = 'a', .long = "aa" }, | ||
| 217 | }, | ||
| 218 | .{ | ||
| 219 | .id = 1, | ||
| 220 | .names = .{ .short = 'c', .long = "cc" }, | ||
| 221 | .takes_value = .one, | ||
| 222 | }, | ||
| 223 | }; | ||
| 224 | |||
| 225 | try testErr(¶ms, &.{"q"}, "Invalid argument 'q'\n"); | ||
| 226 | try testErr(¶ms, &.{"-q"}, "Invalid argument '-q'\n"); | ||
| 227 | try testErr(¶ms, &.{"--q"}, "Invalid argument '--q'\n"); | ||
| 228 | try testErr(¶ms, &.{"--q=1"}, "Invalid argument '--q'\n"); | ||
| 229 | try testErr(¶ms, &.{"-a=1"}, "The argument '-a' does not take a value\n"); | ||
| 230 | try testErr(¶ms, &.{"--aa=1"}, "The argument '--aa' does not take a value\n"); | ||
| 231 | try testErr(¶ms, &.{"-c"}, "The argument '-c' requires a value but none was supplied\n"); | ||
| 232 | try testErr( | ||
| 233 | ¶ms, | ||
| 234 | &.{"--cc"}, | ||
| 235 | "The argument '--cc' requires a value but none was supplied\n", | ||
| 236 | ); | ||
| 237 | } | ||
diff --git a/clap/parsers.zig b/clap/parsers.zig new file mode 100644 index 0000000..49b95a9 --- /dev/null +++ b/clap/parsers.zig | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | const fmt = std.fmt; | ||
| 4 | |||
| 5 | pub const default = .{ | ||
| 6 | .string = string, | ||
| 7 | .str = string, | ||
| 8 | .u8 = int(u8, 0), | ||
| 9 | .u16 = int(u16, 0), | ||
| 10 | .u32 = int(u32, 0), | ||
| 11 | .u64 = int(u64, 0), | ||
| 12 | .usize = int(usize, 0), | ||
| 13 | .i8 = int(i8, 0), | ||
| 14 | .i16 = int(i16, 0), | ||
| 15 | .i32 = int(i32, 0), | ||
| 16 | .i64 = int(i64, 0), | ||
| 17 | .isize = int(isize, 0), | ||
| 18 | .f32 = float(f32), | ||
| 19 | .f64 = float(f64), | ||
| 20 | }; | ||
| 21 | |||
| 22 | pub fn string(in: []const u8) error{}![]const u8 { | ||
| 23 | return in; | ||
| 24 | } | ||
| 25 | |||
| 26 | pub fn int(comptime T: type, comptime radix: u8) fn ([]const u8) fmt.ParseIntError!T { | ||
| 27 | return struct { | ||
| 28 | fn parse(in: []const u8) fmt.ParseIntError!T { | ||
| 29 | return fmt.parseInt(T, in, radix); | ||
| 30 | } | ||
| 31 | }.parse; | ||
| 32 | } | ||
| 33 | |||
| 34 | pub fn float(comptime T: type) fn ([]const u8) fmt.ParseFloatError!T { | ||
| 35 | return struct { | ||
| 36 | fn parse(in: []const u8) fmt.ParseFloatError!T { | ||
| 37 | return fmt.parseFloat(T, in); | ||
| 38 | } | ||
| 39 | }.parse; | ||
| 40 | } | ||
| 41 | |||
| 42 | fn ReturnType(comptime P: type) type { | ||
| 43 | return @typeInfo(P).Fn.return_type.?; | ||
| 44 | } | ||
| 45 | |||
| 46 | pub fn Result(comptime P: type) type { | ||
| 47 | return @typeInfo(ReturnType(P)).ErrorUnion.payload; | ||
| 48 | } | ||
diff --git a/clap/streaming.zig b/clap/streaming.zig index 8eca51a..2ab9c8d 100644 --- a/clap/streaming.zig +++ b/clap/streaming.zig | |||
| @@ -10,7 +10,7 @@ const mem = std.mem; | |||
| 10 | const os = std.os; | 10 | const os = std.os; |
| 11 | const testing = std.testing; | 11 | const testing = std.testing; |
| 12 | 12 | ||
| 13 | /// The result returned from StreamingClap.next | 13 | /// The result returned from Clap.next |
| 14 | pub fn Arg(comptime Id: type) type { | 14 | pub fn Arg(comptime Id: type) type { |
| 15 | return struct { | 15 | return struct { |
| 16 | const Self = @This(); | 16 | const Self = @This(); |
| @@ -20,10 +20,18 @@ pub fn Arg(comptime Id: type) type { | |||
| 20 | }; | 20 | }; |
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | pub const Error = error{ | ||
| 24 | MissingValue, | ||
| 25 | InvalidArgument, | ||
| 26 | DoesntTakeValue, | ||
| 27 | }; | ||
| 28 | |||
| 23 | /// A command line argument parser which, given an ArgIterator, will parse arguments according | 29 | /// A command line argument parser which, given an ArgIterator, will parse arguments according |
| 24 | /// to the params. StreamingClap parses in an iterating manner, so you have to use a loop | 30 | /// to the params. Clap parses in an iterating manner, so you have to use a loop together with |
| 25 | /// together with StreamingClap.next to parse all the arguments of your program. | 31 | /// Clap.next to parse all the arguments of your program. |
| 26 | pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type { | 32 | /// |
| 33 | /// This parser is the building block for all the more complicated parsers. | ||
| 34 | pub fn Clap(comptime Id: type, comptime ArgIterator: type) type { | ||
| 27 | return struct { | 35 | return struct { |
| 28 | const State = union(enum) { | 36 | const State = union(enum) { |
| 29 | normal, | 37 | normal, |
| @@ -71,7 +79,7 @@ pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type { | |||
| 71 | continue; | 79 | continue; |
| 72 | if (param.takes_value == .none) { | 80 | if (param.takes_value == .none) { |
| 73 | if (maybe_value != null) | 81 | if (maybe_value != null) |
| 74 | return parser.err(arg, .{ .long = name }, error.DoesntTakeValue); | 82 | return parser.err(arg, .{ .long = name }, Error.DoesntTakeValue); |
| 75 | 83 | ||
| 76 | return Arg(Id){ .param = param }; | 84 | return Arg(Id){ .param = param }; |
| 77 | } | 85 | } |
| @@ -81,13 +89,13 @@ pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type { | |||
| 81 | break :blk v; | 89 | break :blk v; |
| 82 | 90 | ||
| 83 | break :blk parser.iter.next() orelse | 91 | break :blk parser.iter.next() orelse |
| 84 | return parser.err(arg, .{ .long = name }, error.MissingValue); | 92 | return parser.err(arg, .{ .long = name }, Error.MissingValue); |
| 85 | }; | 93 | }; |
| 86 | 94 | ||
| 87 | return Arg(Id){ .param = param, .value = value }; | 95 | return Arg(Id){ .param = param, .value = value }; |
| 88 | } | 96 | } |
| 89 | 97 | ||
| 90 | return parser.err(arg, .{ .long = name }, error.InvalidArgument); | 98 | return parser.err(arg, .{ .long = name }, Error.InvalidArgument); |
| 91 | }, | 99 | }, |
| 92 | .short => return try parser.chaining(.{ | 100 | .short => return try parser.chaining(.{ |
| 93 | .arg = arg, | 101 | .arg = arg, |
| @@ -105,7 +113,7 @@ pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type { | |||
| 105 | 113 | ||
| 106 | return Arg(Id){ .param = param, .value = arg }; | 114 | return Arg(Id){ .param = param, .value = arg }; |
| 107 | } else { | 115 | } else { |
| 108 | return parser.err(arg, .{}, error.InvalidArgument); | 116 | return parser.err(arg, .{}, Error.InvalidArgument); |
| 109 | }, | 117 | }, |
| 110 | } | 118 | } |
| 111 | } | 119 | } |
| @@ -137,13 +145,13 @@ pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type { | |||
| 137 | const next_is_eql = if (next_index < arg.len) arg[next_index] == '=' else false; | 145 | const next_is_eql = if (next_index < arg.len) arg[next_index] == '=' else false; |
| 138 | if (param.takes_value == .none) { | 146 | if (param.takes_value == .none) { |
| 139 | if (next_is_eql) | 147 | if (next_is_eql) |
| 140 | return parser.err(arg, .{ .short = short }, error.DoesntTakeValue); | 148 | return parser.err(arg, .{ .short = short }, Error.DoesntTakeValue); |
| 141 | return Arg(Id){ .param = param }; | 149 | return Arg(Id){ .param = param }; |
| 142 | } | 150 | } |
| 143 | 151 | ||
| 144 | if (arg.len <= next_index) { | 152 | if (arg.len <= next_index) { |
| 145 | const value = parser.iter.next() orelse | 153 | const value = parser.iter.next() orelse |
| 146 | return parser.err(arg, .{ .short = short }, error.MissingValue); | 154 | return parser.err(arg, .{ .short = short }, Error.MissingValue); |
| 147 | 155 | ||
| 148 | return Arg(Id){ .param = param, .value = value }; | 156 | return Arg(Id){ .param = param, .value = value }; |
| 149 | } | 157 | } |
| @@ -154,7 +162,7 @@ pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type { | |||
| 154 | return Arg(Id){ .param = param, .value = arg[next_index..] }; | 162 | return Arg(Id){ .param = param, .value = arg[next_index..] }; |
| 155 | } | 163 | } |
| 156 | 164 | ||
| 157 | return parser.err(arg, .{ .short = arg[index] }, error.InvalidArgument); | 165 | return parser.err(arg, .{ .short = arg[index] }, Error.InvalidArgument); |
| 158 | } | 166 | } |
| 159 | 167 | ||
| 160 | fn positionalParam(parser: *@This()) ?*const clap.Param(Id) { | 168 | fn positionalParam(parser: *@This()) ?*const clap.Param(Id) { |
| @@ -209,7 +217,7 @@ fn testNoErr( | |||
| 209 | results: []const Arg(u8), | 217 | results: []const Arg(u8), |
| 210 | ) !void { | 218 | ) !void { |
| 211 | var iter = args.SliceIterator{ .args = args_strings }; | 219 | var iter = args.SliceIterator{ .args = args_strings }; |
| 212 | var c = StreamingClap(u8, args.SliceIterator){ | 220 | var c = Clap(u8, args.SliceIterator){ |
| 213 | .params = params, | 221 | .params = params, |
| 214 | .iter = &iter, | 222 | .iter = &iter, |
| 215 | }; | 223 | }; |
| @@ -236,7 +244,7 @@ fn testErr( | |||
| 236 | ) !void { | 244 | ) !void { |
| 237 | var diag: clap.Diagnostic = undefined; | 245 | var diag: clap.Diagnostic = undefined; |
| 238 | var iter = args.SliceIterator{ .args = args_strings }; | 246 | var iter = args.SliceIterator{ .args = args_strings }; |
| 239 | var c = StreamingClap(u8, args.SliceIterator){ | 247 | var c = Clap(u8, args.SliceIterator){ |
| 240 | .params = params, | 248 | .params = params, |
| 241 | .iter = &iter, | 249 | .iter = &iter, |
| 242 | .diagnostic = &diag, | 250 | .diagnostic = &diag, |
diff --git a/example/README.md.template b/example/README.md.template index 7f5c545..9fbc1cc 100644 --- a/example/README.md.template +++ b/example/README.md.template | |||
| @@ -27,33 +27,24 @@ The simplest way to use this library is to just call the `clap.parse` function. | |||
| 27 | {s} | 27 | {s} |
| 28 | ``` | 28 | ``` |
| 29 | 29 | ||
| 30 | The data structure returned has lookup speed on par with array access (`arr[i]`) and validates | 30 | The result will contain an `args` field and a `positionals` field. `args` will have one field |
| 31 | that the strings you pass to `option`, `options` and `flag` are actually parameters that the | 31 | for each none positional parameter of your program. The name of the field will be the longest |
| 32 | program can take: | 32 | name of the parameter. |
| 33 | |||
| 34 | The fields in `args` are typed. The type is based on the name of the value the parameter takes. | ||
| 35 | Since `--number` takes a `usize` the field `res.args.number` has the type `usize`. | ||
| 36 | |||
| 37 | Note that this is only the case because `clap.parsers.default` has a field called `usize` which | ||
| 38 | contains a parser that returns `usize`. You can pass in something other than `clap.parsers.default` | ||
| 39 | if you want some other mapping. | ||
| 33 | 40 | ||
| 34 | ```zig | 41 | ```zig |
| 35 | {s} | 42 | {s} |
| 36 | ``` | 43 | ``` |
| 37 | 44 | ||
| 38 | ``` | 45 | ### `streaming.Clap` |
| 39 | zig-clap/clap/comptime.zig:109:17: error: --helps is not a parameter. | ||
| 40 | @compileError(name ++ " is not a parameter."); | ||
| 41 | ^ | ||
| 42 | zig-clap/clap/comptime.zig:77:45: note: called from here | ||
| 43 | const param = comptime findParam(name); | ||
| 44 | ^ | ||
| 45 | zig-clap/clap.zig:238:31: note: called from here | ||
| 46 | return a.clap.flag(name); | ||
| 47 | ^ | ||
| 48 | zig-clap/example/simple-error.zig:16:18: note: called from here | ||
| 49 | _ = args.flag("--helps"); | ||
| 50 | ``` | ||
| 51 | |||
| 52 | There is also a `parseEx` variant that takes an argument iterator. | ||
| 53 | 46 | ||
| 54 | ### `StreamingClap` | 47 | The `streaming.Clap` is the base of all the other parsers. It's a streaming parser that uses an |
| 55 | |||
| 56 | The `StreamingClap` is the base of all the other parsers. It's a streaming parser that uses an | ||
| 57 | `args.Iterator` to provide it with arguments lazily. | 48 | `args.Iterator` to provide it with arguments lazily. |
| 58 | 49 | ||
| 59 | ```zig | 50 | ```zig |
| @@ -65,8 +56,9 @@ is generated at runtime. | |||
| 65 | 56 | ||
| 66 | ### `help` | 57 | ### `help` |
| 67 | 58 | ||
| 68 | The `help`, `helpEx` and `helpFull` are functions for printing a simple list of all parameters the | 59 | The `help` prints a simple list of all parameters the program can take. It expects the |
| 69 | program can take. | 60 | `Id` to have a `description` method and an `value` method so that it can provide that |
| 61 | in the output. | ||
| 70 | 62 | ||
| 71 | ```zig | 63 | ```zig |
| 72 | {s} | 64 | {s} |
| @@ -78,19 +70,10 @@ $ zig-out/bin/help --help | |||
| 78 | -v, --version Output version information and exit. | 70 | -v, --version Output version information and exit. |
| 79 | ``` | 71 | ``` |
| 80 | 72 | ||
| 81 | The `help` functions are the simplest to call. It only takes an `OutStream` and a slice of | ||
| 82 | `Param(Help)`. | ||
| 83 | |||
| 84 | The `helpEx` is the generic version of `help`. It can print a help message for any | ||
| 85 | `Param` give that the caller provides functions for getting the help and value strings. | ||
| 86 | |||
| 87 | The `helpFull` is even more generic, allowing the functions that get the help and value strings | ||
| 88 | to return errors and take a context as a parameter. | ||
| 89 | |||
| 90 | ### `usage` | 73 | ### `usage` |
| 91 | 74 | ||
| 92 | The `usage`, `usageEx` and `usageFull` are functions for printing a small abbreviated version | 75 | The `usage` prints a small abbreviated version of the help message. It expects the `Id` |
| 93 | of the help message. | 76 | to have a `value` method so it can provide that in the output. |
| 94 | 77 | ||
| 95 | ```zig | 78 | ```zig |
| 96 | {s} | 79 | {s} |
diff --git a/example/help.zig b/example/help.zig index de3b707..f3edb58 100644 --- a/example/help.zig +++ b/example/help.zig | |||
| @@ -7,12 +7,12 @@ pub fn main() !void { | |||
| 7 | clap.parseParam("-v, --version Output version information and exit.") catch unreachable, | 7 | clap.parseParam("-v, --version Output version information and exit.") catch unreachable, |
| 8 | }; | 8 | }; |
| 9 | 9 | ||
| 10 | var args = try clap.parse(clap.Help, ¶ms, .{}); | 10 | var res = try clap.parse(clap.Help, ¶ms, clap.parsers.default, .{}); |
| 11 | defer args.deinit(); | 11 | defer res.deinit(); |
| 12 | 12 | ||
| 13 | // clap.help is a function that can print a simple help message, given a | 13 | // clap.help is a function that can print a simple help message, given a |
| 14 | // slice of Param(Help). There is also a helpEx, which can print a | 14 | // slice of Param(Help). There is also a helpEx, which can print a |
| 15 | // help message for any Param, but it is more verbose to call. | 15 | // help message for any Param, but it is more verbose to call. |
| 16 | if (args.flag("--help")) | 16 | if (res.args.help) |
| 17 | return clap.help(std.io.getStdErr().writer(), ¶ms); | 17 | return clap.help(std.io.getStdErr().writer(), clap.Help, ¶ms); |
| 18 | } | 18 | } |
diff --git a/example/simple-error.zig b/example/simple-error.zig deleted file mode 100644 index c04a9c6..0000000 --- a/example/simple-error.zig +++ /dev/null | |||
| @@ -1,13 +0,0 @@ | |||
| 1 | const clap = @import("clap"); | ||
| 2 | const std = @import("std"); | ||
| 3 | |||
| 4 | pub fn main() !void { | ||
| 5 | const params = comptime [_]clap.Param(clap.Help){ | ||
| 6 | clap.parseParam("-h, --help Display this help and exit.") catch unreachable, | ||
| 7 | }; | ||
| 8 | |||
| 9 | var args = try clap.parse(clap.Help, ¶ms, .{}); | ||
| 10 | defer args.deinit(); | ||
| 11 | |||
| 12 | _ = args.flag("--helps"); | ||
| 13 | } | ||
diff --git a/example/simple-ex.zig b/example/simple-ex.zig index d2dc77e..6cb4c3f 100644 --- a/example/simple-ex.zig +++ b/example/simple-ex.zig | |||
| @@ -6,43 +6,38 @@ const io = std.io; | |||
| 6 | const process = std.process; | 6 | const process = std.process; |
| 7 | 7 | ||
| 8 | pub fn main() !void { | 8 | pub fn main() !void { |
| 9 | const allocator = std.heap.page_allocator; | ||
| 10 | |||
| 11 | // First we specify what parameters our program can take. | 9 | // First we specify what parameters our program can take. |
| 12 | // We can use `parseParam` to parse a string to a `Param(Help)` | 10 | // We can use `parseParam` to parse a string to a `Param(Help)` |
| 13 | const params = comptime [_]clap.Param(clap.Help){ | 11 | const params = comptime [_]clap.Param(clap.Help){ |
| 14 | clap.parseParam("-h, --help Display this help and exit. ") catch unreachable, | 12 | clap.parseParam("-h, --help Display this help and exit.") catch unreachable, |
| 15 | clap.parseParam("-n, --number <NUM> An option parameter, which takes a value.") catch unreachable, | 13 | clap.parseParam("-n, --number <INT> An option parameter, which takes a value.") catch unreachable, |
| 16 | clap.parseParam("-s, --string <STR>... An option parameter which can be specified multiple times.") catch unreachable, | 14 | clap.parseParam("-s, --string <STR>... An option parameter which can be specified multiple times.") catch unreachable, |
| 17 | clap.parseParam("<POS>...") catch unreachable, | 15 | clap.parseParam("<FILE>...") catch unreachable, |
| 18 | }; | 16 | }; |
| 19 | 17 | ||
| 20 | var iter = try process.ArgIterator.initWithAllocator(allocator); | 18 | // Declare our own parsers which are used to map the argument strings to other |
| 21 | defer iter.deinit(); | 19 | // types. |
| 22 | 20 | const parsers = comptime .{ | |
| 23 | // Skip exe argument | 21 | .STR = clap.parsers.string, |
| 24 | _ = iter.next(); | 22 | .FILE = clap.parsers.string, |
| 23 | .INT = clap.parsers.int(usize, 10), | ||
| 24 | }; | ||
| 25 | 25 | ||
| 26 | // Initalize our diagnostics, which can be used for reporting useful errors. | ||
| 27 | // This is optional. You can also pass `.{}` to `clap.parse` if you don't | ||
| 28 | // care about the extra information `Diagnostics` provides. | ||
| 29 | var diag = clap.Diagnostic{}; | 26 | var diag = clap.Diagnostic{}; |
| 30 | var args = clap.parseEx(clap.Help, ¶ms, &iter, .{ | 27 | var res = clap.parse(clap.Help, ¶ms, parsers, .{ |
| 31 | .allocator = allocator, | ||
| 32 | .diagnostic = &diag, | 28 | .diagnostic = &diag, |
| 33 | }) catch |err| { | 29 | }) catch |err| { |
| 34 | // Report useful error and exit | ||
| 35 | diag.report(io.getStdErr().writer(), err) catch {}; | 30 | diag.report(io.getStdErr().writer(), err) catch {}; |
| 36 | return err; | 31 | return err; |
| 37 | }; | 32 | }; |
| 38 | defer args.deinit(); | 33 | defer res.deinit(); |
| 39 | 34 | ||
| 40 | if (args.flag("--help")) | 35 | if (res.args.help) |
| 41 | debug.print("--help\n", .{}); | 36 | debug.print("--help\n", .{}); |
| 42 | if (args.option("--number")) |n| | 37 | if (res.args.number) |n| |
| 43 | debug.print("--number = {s}\n", .{n}); | 38 | debug.print("--number = {}\n", .{n}); |
| 44 | for (args.options("--string")) |s| | 39 | for (res.args.string) |s| |
| 45 | debug.print("--string = {s}\n", .{s}); | 40 | debug.print("--string = {s}\n", .{s}); |
| 46 | for (args.positionals()) |pos| | 41 | for (res.positionals) |pos| |
| 47 | debug.print("{s}\n", .{pos}); | 42 | debug.print("{s}\n", .{pos}); |
| 48 | } | 43 | } |
diff --git a/example/simple.zig b/example/simple.zig index ff6d301..11e975e 100644 --- a/example/simple.zig +++ b/example/simple.zig | |||
| @@ -8,29 +8,31 @@ pub fn main() !void { | |||
| 8 | // First we specify what parameters our program can take. | 8 | // First we specify what parameters our program can take. |
| 9 | // We can use `parseParam` to parse a string to a `Param(Help)` | 9 | // We can use `parseParam` to parse a string to a `Param(Help)` |
| 10 | const params = comptime [_]clap.Param(clap.Help){ | 10 | const params = comptime [_]clap.Param(clap.Help){ |
| 11 | clap.parseParam("-h, --help Display this help and exit. ") catch unreachable, | 11 | clap.parseParam("-h, --help Display this help and exit.") catch unreachable, |
| 12 | clap.parseParam("-n, --number <NUM> An option parameter, which takes a value.") catch unreachable, | 12 | clap.parseParam("-n, --number <usize> An option parameter, which takes a value.") catch unreachable, |
| 13 | clap.parseParam("-s, --string <STR>... An option parameter which can be specified multiple times.") catch unreachable, | 13 | clap.parseParam("-s, --string <str>... An option parameter which can be specified multiple times.") catch unreachable, |
| 14 | clap.parseParam("<POS>...") catch unreachable, | 14 | clap.parseParam("<str>...") catch unreachable, |
| 15 | }; | 15 | }; |
| 16 | 16 | ||
| 17 | // Initalize our diagnostics, which can be used for reporting useful errors. | 17 | // Initalize our diagnostics, which can be used for reporting useful errors. |
| 18 | // This is optional. You can also pass `.{}` to `clap.parse` if you don't | 18 | // This is optional. You can also pass `.{}` to `clap.parse` if you don't |
| 19 | // care about the extra information `Diagnostics` provides. | 19 | // care about the extra information `Diagnostics` provides. |
| 20 | var diag = clap.Diagnostic{}; | 20 | var diag = clap.Diagnostic{}; |
| 21 | var args = clap.parse(clap.Help, ¶ms, .{ .diagnostic = &diag }) catch |err| { | 21 | var res = clap.parse(clap.Help, ¶ms, clap.parsers.default, .{ |
| 22 | .diagnostic = &diag, | ||
| 23 | }) catch |err| { | ||
| 22 | // Report useful error and exit | 24 | // Report useful error and exit |
| 23 | diag.report(io.getStdErr().writer(), err) catch {}; | 25 | diag.report(io.getStdErr().writer(), err) catch {}; |
| 24 | return err; | 26 | return err; |
| 25 | }; | 27 | }; |
| 26 | defer args.deinit(); | 28 | defer res.deinit(); |
| 27 | 29 | ||
| 28 | if (args.flag("--help")) | 30 | if (res.args.help) |
| 29 | debug.print("--help\n", .{}); | 31 | debug.print("--help\n", .{}); |
| 30 | if (args.option("--number")) |n| | 32 | if (res.args.number) |n| |
| 31 | debug.print("--number = {s}\n", .{n}); | 33 | debug.print("--number = {}\n", .{n}); |
| 32 | for (args.options("--string")) |s| | 34 | for (res.args.string) |s| |
| 33 | debug.print("--string = {s}\n", .{s}); | 35 | debug.print("--string = {s}\n", .{s}); |
| 34 | for (args.positionals()) |pos| | 36 | for (res.positionals) |pos| |
| 35 | debug.print("{s}\n", .{pos}); | 37 | debug.print("{s}\n", .{pos}); |
| 36 | } | 38 | } |
diff --git a/example/streaming-clap.zig b/example/streaming-clap.zig index a7ab7d8..cacda56 100644 --- a/example/streaming-clap.zig +++ b/example/streaming-clap.zig | |||
| @@ -32,7 +32,7 @@ pub fn main() !void { | |||
| 32 | // This is optional. You can also leave the `diagnostic` field unset if you | 32 | // This is optional. You can also leave the `diagnostic` field unset if you |
| 33 | // don't care about the extra information `Diagnostic` provides. | 33 | // don't care about the extra information `Diagnostic` provides. |
| 34 | var diag = clap.Diagnostic{}; | 34 | var diag = clap.Diagnostic{}; |
| 35 | var parser = clap.StreamingClap(u8, process.ArgIterator){ | 35 | var parser = clap.streaming.Clap(u8, process.ArgIterator){ |
| 36 | .params = ¶ms, | 36 | .params = ¶ms, |
| 37 | .iter = &iter, | 37 | .iter = &iter, |
| 38 | .diagnostic = &diag, | 38 | .diagnostic = &diag, |
diff --git a/example/usage.zig b/example/usage.zig index 368a6b3..20d4736 100644 --- a/example/usage.zig +++ b/example/usage.zig | |||
| @@ -3,17 +3,17 @@ const std = @import("std"); | |||
| 3 | 3 | ||
| 4 | pub fn main() !void { | 4 | pub fn main() !void { |
| 5 | const params = comptime [_]clap.Param(clap.Help){ | 5 | const params = comptime [_]clap.Param(clap.Help){ |
| 6 | clap.parseParam("-h, --help Display this help and exit. ") catch unreachable, | 6 | clap.parseParam("-h, --help Display this help and exit.") catch unreachable, |
| 7 | clap.parseParam("-v, --version Output version information and exit. ") catch unreachable, | 7 | clap.parseParam("-v, --version Output version information and exit.") catch unreachable, |
| 8 | clap.parseParam(" --value <N> An option parameter, which takes a value.") catch unreachable, | 8 | clap.parseParam(" --value <str> An option parameter, which takes a value.") catch unreachable, |
| 9 | }; | 9 | }; |
| 10 | 10 | ||
| 11 | var args = try clap.parse(clap.Help, ¶ms, .{}); | 11 | var res = try clap.parse(clap.Help, ¶ms, clap.parsers.default, .{}); |
| 12 | defer args.deinit(); | 12 | defer res.deinit(); |
| 13 | 13 | ||
| 14 | // clap.usage is a function that can print a simple usage message, given a | 14 | // clap.usage is a function that can print a simple usage message, given a |
| 15 | // slice of Param(Help). There is also a usageEx, which can print a | 15 | // slice of Param(Help). There is also a usageEx, which can print a |
| 16 | // usage message for any Param, but it is more verbose to call. | 16 | // usage message for any Param, but it is more verbose to call. |
| 17 | if (args.flag("--help")) | 17 | if (res.args.help) |
| 18 | return clap.usage(std.io.getStdErr().writer(), ¶ms); | 18 | return clap.usage(std.io.getStdErr().writer(), clap.Help, ¶ms); |
| 19 | } | 19 | } |