diff options
| -rw-r--r-- | README.md | 237 | ||||
| -rw-r--r-- | build.zig | 27 | ||||
| -rw-r--r-- | clap.zig | 10 | ||||
| -rw-r--r-- | example/README.md.template | 77 | ||||
| -rw-r--r-- | example/comptime-clap-error.zig | 21 | ||||
| -rw-r--r-- | example/comptime-clap.zig | 7 | ||||
| -rw-r--r-- | example/help.zig | 25 | ||||
| -rw-r--r-- | src/comptime.zig | 19 | ||||
| -rw-r--r-- | src/streaming.zig | 30 |
9 files changed, 346 insertions, 107 deletions
| @@ -18,27 +18,64 @@ A simple and easy to use command line argument parser library for Zig. | |||
| 18 | The `StreamingClap` is the base of all the other parsers. It's a streaming parser that uses an | 18 | The `StreamingClap` is the base of all the other parsers. It's a streaming parser that uses an |
| 19 | `args.Iterator` to provide it with arguments lazily. | 19 | `args.Iterator` to provide it with arguments lazily. |
| 20 | 20 | ||
| 21 | ```rust | 21 | ```zig |
| 22 | const params = []clap.Param(u8){ | 22 | const std = @import("std"); |
| 23 | clap.Param(u8).flag('h', clap.Names.both("help")), | 23 | const clap = @import("clap"); |
| 24 | clap.Param(u8).option('n', clap.Names.both("number")), | 24 | |
| 25 | clap.Param(u8).positional('f'), | 25 | const debug = std.debug; |
| 26 | }; | 26 | |
| 27 | 27 | pub fn main() !void { | |
| 28 | var iter = clap.args.OsIterator.init(allocator); | 28 | var direct_allocator = std.heap.DirectAllocator.init(); |
| 29 | defer iter.deinit(); | 29 | const allocator = &direct_allocator.allocator; |
| 30 | const exe = try iter.next(); | 30 | defer direct_allocator.deinit(); |
| 31 | 31 | ||
| 32 | var parser = clap.StreamingClap(u8, clap.args.OsIterator).init(params, &iter); | 32 | // First we specify what parameters our program can take. |
| 33 | 33 | const params = [_]clap.Param(u8){ | |
| 34 | while (try parser.next()) |arg| { | 34 | clap.Param(u8){ |
| 35 | switch (arg.param.id) { | 35 | .id = 'h', |
| 36 | 'h' => debug.warn("Help!\n"), | 36 | .names = clap.Names{ .short = 'h', .long = "help" }, |
| 37 | 'n' => debug.warn("--number = {}\n", arg.value.?), | 37 | }, |
| 38 | 'f' => debug.warn("{}\n", arg.value.?), | 38 | clap.Param(u8){ |
| 39 | else => unreachable, | 39 | .id = 'n', |
| 40 | .names = clap.Names{ .short = 'n', .long = "number" }, | ||
| 41 | .takes_value = true, | ||
| 42 | }, | ||
| 43 | clap.Param(u8){ | ||
| 44 | .id = 'f', | ||
| 45 | .takes_value = true, | ||
| 46 | }, | ||
| 47 | }; | ||
| 48 | |||
| 49 | // We then initialize an argument iterator. We will use the OsIterator as it nicely | ||
| 50 | // wraps iterating over arguments the most efficient way on each os. | ||
| 51 | var iter = clap.args.OsIterator.init(allocator); | ||
| 52 | defer iter.deinit(); | ||
| 53 | |||
| 54 | // Consume the exe arg. | ||
| 55 | const exe = try iter.next(); | ||
| 56 | |||
| 57 | // Finally we initialize our streaming parser. | ||
| 58 | var parser = clap.StreamingClap(u8, clap.args.OsIterator){ | ||
| 59 | .params = params, | ||
| 60 | .iter = &iter, | ||
| 61 | }; | ||
| 62 | |||
| 63 | // Because we use a streaming parser, we have to consume each argument parsed individually. | ||
| 64 | while (try parser.next()) |arg| { | ||
| 65 | // arg.param will point to the parameter which matched the argument. | ||
| 66 | switch (arg.param.id) { | ||
| 67 | 'h' => debug.warn("Help!\n"), | ||
| 68 | 'n' => debug.warn("--number = {}\n", arg.value.?), | ||
| 69 | |||
| 70 | // arg.value == null, if arg.param.takes_value == false. | ||
| 71 | // Otherwise, arg.value is the value passed with the argument, such as "-a=10" | ||
| 72 | // or "-a 10". | ||
| 73 | 'f' => debug.warn("{}\n", arg.value.?), | ||
| 74 | else => unreachable, | ||
| 75 | } | ||
| 40 | } | 76 | } |
| 41 | } | 77 | } |
| 78 | |||
| 42 | ``` | 79 | ``` |
| 43 | 80 | ||
| 44 | ### `ComptimeClap` | 81 | ### `ComptimeClap` |
| @@ -46,57 +83,100 @@ while (try parser.next()) |arg| { | |||
| 46 | The `ComptimeClap` is a wrapper for `StreamingClap`, which parses all the arguments and makes | 83 | The `ComptimeClap` is a wrapper for `StreamingClap`, which parses all the arguments and makes |
| 47 | them available through three functions (`flag`, `option`, `positionals`). | 84 | them available through three functions (`flag`, `option`, `positionals`). |
| 48 | 85 | ||
| 49 | ```rust | 86 | ```zig |
| 50 | const params = comptime []clap.Param(void){ | 87 | const std = @import("std"); |
| 51 | clap.Param(void).flag({}, clap.Names.both("help")), | 88 | const clap = @import("clap"); |
| 52 | clap.Param(void).option({}, clap.Names.both("number")), | 89 | |
| 53 | clap.Param(void).positional({}), | 90 | const debug = std.debug; |
| 54 | }; | 91 | |
| 55 | 92 | pub fn main() !void { | |
| 56 | var iter = clap.args.OsIterator.init(allocator); | 93 | const stdout_file = try std.io.getStdOut(); |
| 57 | defer iter.deinit(); | 94 | var stdout_out_stream = stdout_file.outStream(); |
| 58 | const exe = try iter.next(); | 95 | const stdout = &stdout_out_stream.stream; |
| 59 | 96 | ||
| 60 | var args = try clap.ComptimeClap(void, params).parse(allocator, clap.args.OsIterator, &iter); | 97 | var direct_allocator = std.heap.DirectAllocator.init(); |
| 61 | defer args.deinit(); | 98 | const allocator = &direct_allocator.allocator; |
| 62 | 99 | defer direct_allocator.deinit(); | |
| 63 | if (args.flag("--help")) | 100 | |
| 64 | debug.warn("Help!\n"); | 101 | // First we specify what parameters our program can take. |
| 65 | if (args.option("--number")) |n| | 102 | const params = [_]clap.Param([]const u8){ |
| 66 | debug.warn("--number = {}\n", n); | 103 | clap.Param([]const u8){ |
| 67 | for (args.positionals()) |pos| | 104 | .id = "Display this help and exit.", |
| 68 | debug.warn("{}\n", pos); | 105 | .names = clap.Names{ .short = 'h', .long = "help" }, |
| 106 | }, | ||
| 107 | clap.Param([]const u8){ | ||
| 108 | .id = "An option parameter, which takes a value.", | ||
| 109 | .names = clap.Names{ .short = 'n', .long = "number" }, | ||
| 110 | .takes_value = true, | ||
| 111 | }, | ||
| 112 | clap.Param([]const u8){ | ||
| 113 | .id = "", | ||
| 114 | .takes_value = true, | ||
| 115 | }, | ||
| 116 | }; | ||
| 117 | |||
| 118 | // We then initialize an argument iterator. We will use the OsIterator as it nicely | ||
| 119 | // wraps iterating over arguments the most efficient way on each os. | ||
| 120 | var iter = clap.args.OsIterator.init(allocator); | ||
| 121 | defer iter.deinit(); | ||
| 122 | |||
| 123 | // Consume the exe arg. | ||
| 124 | const exe = try iter.next(); | ||
| 125 | |||
| 126 | // Finally we can parse the arguments | ||
| 127 | var args = try clap.ComptimeClap([]const u8, params).parse(allocator, clap.args.OsIterator, &iter); | ||
| 128 | defer args.deinit(); | ||
| 129 | |||
| 130 | if (args.flag("--help")) | ||
| 131 | debug.warn("--help\n"); | ||
| 132 | if (args.option("--number")) |n| | ||
| 133 | debug.warn("--number = {}\n", n); | ||
| 134 | for (args.positionals()) |pos| | ||
| 135 | debug.warn("{}\n", pos); | ||
| 136 | } | ||
| 137 | |||
| 69 | ``` | 138 | ``` |
| 70 | 139 | ||
| 71 | The data structure returned from this parser has lookup speed on par with array access (`arr[i]`) | 140 | The data structure returned from this parser has lookup speed on par with array access (`arr[i]`) |
| 72 | and validates that the strings you pass to `option` and `flag` are actually parameters that the | 141 | and validates that the strings you pass to `option` and `flag` are actually parameters that the |
| 73 | program can take: | 142 | program can take: |
| 74 | 143 | ||
| 75 | ```rust | 144 | ```zig |
| 76 | const params = comptime []clap.Param(void){ | 145 | const std = @import("std"); |
| 77 | clap.Param(void).flag({}, clap.Names.both("help")), | 146 | const clap = @import("clap"); |
| 78 | }; | 147 | |
| 148 | pub fn main() !void { | ||
| 149 | const params = [_]clap.Param(void){ | ||
| 150 | clap.Param(void){ | ||
| 151 | .names = clap.Names{ .short = 'h', .long = "help" } | ||
| 152 | }, | ||
| 153 | }; | ||
| 154 | |||
| 155 | var direct_allocator = std.heap.DirectAllocator.init(); | ||
| 156 | const allocator = &direct_allocator.allocator; | ||
| 157 | defer direct_allocator.deinit(); | ||
| 79 | 158 | ||
| 80 | var iter = clap.args.OsIterator.init(allocator); | 159 | var iter = clap.args.OsIterator.init(allocator); |
| 81 | defer iter.deinit(); | 160 | defer iter.deinit(); |
| 82 | const exe = try iter.next(); | 161 | const exe = try iter.next(); |
| 83 | 162 | ||
| 84 | var args = try clap.ComptimeClap(void, params).parse(allocator, clap.args.OsIterator, &iter); | 163 | var args = try clap.ComptimeClap(void, params).parse(allocator, clap.args.OsIterator, &iter); |
| 85 | defer args.deinit(); | 164 | defer args.deinit(); |
| 165 | |||
| 166 | _ = args.flag("--helps"); | ||
| 167 | } | ||
| 86 | 168 | ||
| 87 | if (args.flag("--helps")) | ||
| 88 | debug.warn("Help!\n"); | ||
| 89 | ``` | 169 | ``` |
| 90 | 170 | ||
| 91 | ``` | 171 | ``` |
| 92 | zig-clap/src/comptime.zig:103:17: error: --helps is not a parameter. | 172 | zig-clap/src/comptime.zig:116:17: error: --helps is not a parameter. |
| 93 | @compileError(name ++ " is not a parameter."); | 173 | @compileError(name ++ " is not a parameter."); |
| 94 | ^ | 174 | ^ |
| 95 | zig-clap/src/comptime.zig:71:45: note: called from here | 175 | zig-clap/src/comptime.zig:84:45: note: called from here |
| 96 | const param = comptime findParam(name); | 176 | const param = comptime findParam(name); |
| 97 | ^ | 177 | ^ |
| 98 | zig-clap/example/comptime-clap.zig:41:18: note: called from here | 178 | zig-clap/example/comptime-clap-error.zig:22:18: note: called from here |
| 99 | if (args.flag("--helps")) | 179 | _ = args.flag("--helps"); |
| 100 | ^ | 180 | ^ |
| 101 | ``` | 181 | ``` |
| 102 | 182 | ||
| @@ -107,29 +187,38 @@ Ofc, this limits you to parameters that are comptime known. | |||
| 107 | The `help`, `helpEx` and `helpFull` are functions for printing a simple list of all parameters the | 187 | The `help`, `helpEx` and `helpFull` are functions for printing a simple list of all parameters the |
| 108 | program can take. | 188 | program can take. |
| 109 | 189 | ||
| 110 | ```rust | 190 | ```zig |
| 111 | const stderr_file = try std.io.getStdErr(); | 191 | const std = @import("std"); |
| 112 | var stderr_out_stream = stderr_file.outStream(); | 192 | const clap = @import("clap"); |
| 113 | const stderr = &stderr_out_stream.stream; | 193 | |
| 114 | 194 | pub fn main() !void { | |
| 115 | try clap.help( | 195 | const stderr_file = try std.io.getStdErr(); |
| 116 | stderr, | 196 | var stderr_out_stream = stderr_file.outStream(); |
| 117 | []clap.Param([]const u8){ | 197 | const stderr = &stderr_out_stream.stream; |
| 118 | clap.Param([]const u8).flag( | 198 | |
| 119 | "Display this help and exit.", | 199 | // clap.help is a function that can print a simple help message, given a |
| 120 | clap.Names.both("help"), | 200 | // slice of Param([]const u8). There is also a helpEx, which can print a |
| 121 | ), | 201 | // help message for any Param, but it is more verbose to call. |
| 122 | clap.Param([]const u8).flag( | 202 | try clap.help( |
| 123 | "Output version information and exit.", | 203 | stderr, |
| 124 | clap.Names.both("version"), | 204 | [_]clap.Param([]const u8){ |
| 125 | ), | 205 | clap.Param([]const u8){ |
| 126 | }, | 206 | .id = "Display this help and exit.", |
| 127 | ); | 207 | .names = clap.Names{ .short = 'h', .long = "help" } |
| 208 | }, | ||
| 209 | clap.Param([]const u8){ | ||
| 210 | .id = "Output version information and exit.", | ||
| 211 | .names = clap.Names{ .short = 'v', .long = "version" } | ||
| 212 | }, | ||
| 213 | }, | ||
| 214 | ); | ||
| 215 | } | ||
| 216 | |||
| 128 | ``` | 217 | ``` |
| 129 | 218 | ||
| 130 | ``` | 219 | ``` |
| 131 | -h, --help Display this help and exit. | 220 | -h, --help Display this help and exit. |
| 132 | -v, --version Output version information and exit. | 221 | -v, --version Output version information and exit. |
| 133 | ``` | 222 | ``` |
| 134 | 223 | ||
| 135 | The `help` function is the simplest to call. It only takes an `OutStream` and a slice of | 224 | The `help` function is the simplest to call. It only takes an `OutStream` and a slice of |
| @@ -10,7 +10,9 @@ pub fn build(b: *Builder) void { | |||
| 10 | const example_step = b.step("examples", "Build examples"); | 10 | const example_step = b.step("examples", "Build examples"); |
| 11 | inline for ([_][]const u8{ | 11 | inline for ([_][]const u8{ |
| 12 | "comptime-clap", | 12 | "comptime-clap", |
| 13 | //"comptime-clap-error", | ||
| 13 | "streaming-clap", | 14 | "streaming-clap", |
| 15 | "help", | ||
| 14 | }) |example_name| { | 16 | }) |example_name| { |
| 15 | const example = b.addExecutable(example_name, "example/" ++ example_name ++ ".zig"); | 17 | const example = b.addExecutable(example_name, "example/" ++ example_name ++ ".zig"); |
| 16 | example.addPackagePath("clap", "clap.zig"); | 18 | example.addPackagePath("clap", "clap.zig"); |
| @@ -31,13 +33,38 @@ pub fn build(b: *Builder) void { | |||
| 31 | test_all_step.dependOn(test_step); | 33 | test_all_step.dependOn(test_step); |
| 32 | } | 34 | } |
| 33 | 35 | ||
| 36 | const readme_step = b.step("test", "Remake README."); | ||
| 37 | const readme = readMeStep(b); | ||
| 38 | readme.dependOn(example_step); | ||
| 39 | readme_step.dependOn(readme); | ||
| 40 | |||
| 34 | const all_step = b.step("all", "Build everything and runs all tests"); | 41 | const all_step = b.step("all", "Build everything and runs all tests"); |
| 35 | all_step.dependOn(test_all_step); | 42 | all_step.dependOn(test_all_step); |
| 36 | all_step.dependOn(example_step); | 43 | all_step.dependOn(example_step); |
| 44 | all_step.dependOn(readme_step); | ||
| 37 | 45 | ||
| 38 | b.default_step.dependOn(all_step); | 46 | b.default_step.dependOn(all_step); |
| 39 | } | 47 | } |
| 40 | 48 | ||
| 49 | fn readMeStep(b: *Builder) *std.build.Step { | ||
| 50 | const s = b.allocator.create(std.build.Step) catch unreachable; | ||
| 51 | s.* = std.build.Step.init("ReadMeStep", b.allocator, struct { | ||
| 52 | fn make(step: *std.build.Step) anyerror!void { | ||
| 53 | @setEvalBranchQuota(10000); | ||
| 54 | const file = try std.fs.File.openWrite("README.md"); | ||
| 55 | const stream = &file.outStream().stream; | ||
| 56 | try stream.print( | ||
| 57 | @embedFile("example/README.md.template"), | ||
| 58 | @embedFile("example/streaming-clap.zig"), | ||
| 59 | @embedFile("example/comptime-clap.zig"), | ||
| 60 | @embedFile("example/comptime-clap-error.zig"), | ||
| 61 | @embedFile("example/help.zig"), | ||
| 62 | ); | ||
| 63 | } | ||
| 64 | }.make); | ||
| 65 | return s; | ||
| 66 | } | ||
| 67 | |||
| 41 | fn modeToString(mode: Mode) []const u8 { | 68 | fn modeToString(mode: Mode) []const u8 { |
| 42 | return switch (mode) { | 69 | return switch (mode) { |
| 43 | Mode.Debug => "debug", | 70 | Mode.Debug => "debug", |
| @@ -4,18 +4,16 @@ const debug = std.debug; | |||
| 4 | const io = std.io; | 4 | const io = std.io; |
| 5 | const mem = std.mem; | 5 | const mem = std.mem; |
| 6 | 6 | ||
| 7 | pub const @"comptime" = @import("src/comptime.zig"); | ||
| 8 | pub const args = @import("src/args.zig"); | 7 | pub const args = @import("src/args.zig"); |
| 9 | pub const streaming = @import("src/streaming.zig"); | ||
| 10 | 8 | ||
| 11 | test "clap" { | 9 | test "clap" { |
| 12 | _ = @"comptime"; | ||
| 13 | _ = args; | 10 | _ = args; |
| 14 | _ = streaming; | 11 | _ = ComptimeClap; |
| 12 | _ = StreamingClap; | ||
| 15 | } | 13 | } |
| 16 | 14 | ||
| 17 | pub const ComptimeClap = @"comptime".ComptimeClap; | 15 | pub const ComptimeClap = @import("src/comptime.zig").ComptimeClap; |
| 18 | pub const StreamingClap = streaming.StreamingClap; | 16 | pub const StreamingClap = @import("src/streaming.zig").StreamingClap; |
| 19 | 17 | ||
| 20 | /// The names a ::Param can have. | 18 | /// The names a ::Param can have. |
| 21 | pub const Names = struct { | 19 | pub const Names = struct { |
diff --git a/example/README.md.template b/example/README.md.template new file mode 100644 index 0000000..88914fb --- /dev/null +++ b/example/README.md.template | |||
| @@ -0,0 +1,77 @@ | |||
| 1 | # zig-clap | ||
| 2 | |||
| 3 | A simple and easy to use command line argument parser library for Zig. | ||
| 4 | |||
| 5 | ## Features | ||
| 6 | |||
| 7 | * Short arguments `-a` | ||
| 8 | * Chaining `-abc` where `a` and `b` does not take values. | ||
| 9 | * Long arguments `--long` | ||
| 10 | * Supports both passing values using spacing and `=` (`-a 100`, `-a=100`) | ||
| 11 | * Short args also support passing values with no spacing or `=` (`-a100`) | ||
| 12 | * This all works with chaining (`-ba 100`, `-ba=100`, `-ba100`) | ||
| 13 | |||
| 14 | ## Examples | ||
| 15 | |||
| 16 | ### `StreamingClap` | ||
| 17 | |||
| 18 | The `StreamingClap` is the base of all the other parsers. It's a streaming parser that uses an | ||
| 19 | `args.Iterator` to provide it with arguments lazily. | ||
| 20 | |||
| 21 | ```zig | ||
| 22 | {} | ||
| 23 | ``` | ||
| 24 | |||
| 25 | ### `ComptimeClap` | ||
| 26 | |||
| 27 | The `ComptimeClap` is a wrapper for `StreamingClap`, which parses all the arguments and makes | ||
| 28 | them available through three functions (`flag`, `option`, `positionals`). | ||
| 29 | |||
| 30 | ```zig | ||
| 31 | {} | ||
| 32 | ``` | ||
| 33 | |||
| 34 | The data structure returned from this parser has lookup speed on par with array access (`arr[i]`) | ||
| 35 | and validates that the strings you pass to `option` and `flag` are actually parameters that the | ||
| 36 | program can take: | ||
| 37 | |||
| 38 | ```zig | ||
| 39 | {} | ||
| 40 | ``` | ||
| 41 | |||
| 42 | ``` | ||
| 43 | zig-clap/src/comptime.zig:116:17: error: --helps is not a parameter. | ||
| 44 | @compileError(name ++ " is not a parameter."); | ||
| 45 | ^ | ||
| 46 | zig-clap/src/comptime.zig:84:45: note: called from here | ||
| 47 | const param = comptime findParam(name); | ||
| 48 | ^ | ||
| 49 | zig-clap/example/comptime-clap-error.zig:22:18: note: called from here | ||
| 50 | _ = args.flag("--helps"); | ||
| 51 | ^ | ||
| 52 | ``` | ||
| 53 | |||
| 54 | Ofc, this limits you to parameters that are comptime known. | ||
| 55 | |||
| 56 | ### `help` | ||
| 57 | |||
| 58 | The `help`, `helpEx` and `helpFull` are functions for printing a simple list of all parameters the | ||
| 59 | program can take. | ||
| 60 | |||
| 61 | ```zig | ||
| 62 | {} | ||
| 63 | ``` | ||
| 64 | |||
| 65 | ``` | ||
| 66 | -h, --help Display this help and exit. | ||
| 67 | -v, --version Output version information and exit. | ||
| 68 | ``` | ||
| 69 | |||
| 70 | The `help` function is 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. | ||
| 72 | |||
| 73 | The `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. | ||
| 75 | |||
| 76 | The `helpFull` is even more generic, allowing the functions that get the help and value strings | ||
| 77 | to return errors and take a context as a parameter. | ||
diff --git a/example/comptime-clap-error.zig b/example/comptime-clap-error.zig new file mode 100644 index 0000000..93c1af2 --- /dev/null +++ b/example/comptime-clap-error.zig | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | const std = @import("std"); | ||
| 2 | const clap = @import("clap"); | ||
| 3 | |||
| 4 | pub fn main() !void { | ||
| 5 | const params = [_]clap.Param(void){clap.Param(void){ | ||
| 6 | .names = clap.Names{ .short = 'h', .long = "help" }, | ||
| 7 | }}; | ||
| 8 | |||
| 9 | var direct_allocator = std.heap.DirectAllocator.init(); | ||
| 10 | const allocator = &direct_allocator.allocator; | ||
| 11 | defer direct_allocator.deinit(); | ||
| 12 | |||
| 13 | var iter = clap.args.OsIterator.init(allocator); | ||
| 14 | defer iter.deinit(); | ||
| 15 | const exe = try iter.next(); | ||
| 16 | |||
| 17 | var args = try clap.ComptimeClap(void, params).parse(allocator, clap.args.OsIterator, &iter); | ||
| 18 | defer args.deinit(); | ||
| 19 | |||
| 20 | _ = args.flag("--helps"); | ||
| 21 | } | ||
diff --git a/example/comptime-clap.zig b/example/comptime-clap.zig index 935381f..695fa62 100644 --- a/example/comptime-clap.zig +++ b/example/comptime-clap.zig | |||
| @@ -13,7 +13,7 @@ pub fn main() !void { | |||
| 13 | defer direct_allocator.deinit(); | 13 | defer direct_allocator.deinit(); |
| 14 | 14 | ||
| 15 | // First we specify what parameters our program can take. | 15 | // First we specify what parameters our program can take. |
| 16 | const params = comptime [_]clap.Param([]const u8){ | 16 | const params = [_]clap.Param([]const u8){ |
| 17 | clap.Param([]const u8){ | 17 | clap.Param([]const u8){ |
| 18 | .id = "Display this help and exit.", | 18 | .id = "Display this help and exit.", |
| 19 | .names = clap.Names{ .short = 'h', .long = "help" }, | 19 | .names = clap.Names{ .short = 'h', .long = "help" }, |
| @@ -41,11 +41,8 @@ pub fn main() !void { | |||
| 41 | var args = try clap.ComptimeClap([]const u8, params).parse(allocator, clap.args.OsIterator, &iter); | 41 | var args = try clap.ComptimeClap([]const u8, params).parse(allocator, clap.args.OsIterator, &iter); |
| 42 | defer args.deinit(); | 42 | defer args.deinit(); |
| 43 | 43 | ||
| 44 | // clap.help is a function that can print a simple help message, given a | ||
| 45 | // slice of Param([]const u8). There is also a helpEx, which can print a | ||
| 46 | // help message for any Param, but it is more verbose to call. | ||
| 47 | if (args.flag("--help")) | 44 | if (args.flag("--help")) |
| 48 | return try clap.help(stdout, params); | 45 | debug.warn("--help\n"); |
| 49 | if (args.option("--number")) |n| | 46 | if (args.option("--number")) |n| |
| 50 | debug.warn("--number = {}\n", n); | 47 | debug.warn("--number = {}\n", n); |
| 51 | for (args.positionals()) |pos| | 48 | for (args.positionals()) |pos| |
diff --git a/example/help.zig b/example/help.zig new file mode 100644 index 0000000..35c0258 --- /dev/null +++ b/example/help.zig | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | const std = @import("std"); | ||
| 2 | const clap = @import("clap"); | ||
| 3 | |||
| 4 | pub fn main() !void { | ||
| 5 | const stderr_file = try std.io.getStdErr(); | ||
| 6 | var stderr_out_stream = stderr_file.outStream(); | ||
| 7 | const stderr = &stderr_out_stream.stream; | ||
| 8 | |||
| 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 | ||
| 11 | // help message for any Param, but it is more verbose to call. | ||
| 12 | try clap.help( | ||
| 13 | stderr, | ||
| 14 | [_]clap.Param([]const u8){ | ||
| 15 | clap.Param([]const u8){ | ||
| 16 | .id = "Display this help and exit.", | ||
| 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 | }, | ||
| 24 | ); | ||
| 25 | } | ||
diff --git a/src/comptime.zig b/src/comptime.zig index b585598..d872b82 100644 --- a/src/comptime.zig +++ b/src/comptime.zig | |||
| @@ -4,6 +4,7 @@ const std = @import("std"); | |||
| 4 | const testing = std.testing; | 4 | const testing = std.testing; |
| 5 | const heap = std.heap; | 5 | const heap = std.heap; |
| 6 | const mem = std.mem; | 6 | const mem = std.mem; |
| 7 | const debug = std.debug; | ||
| 7 | 8 | ||
| 8 | pub fn ComptimeClap(comptime Id: type, comptime params: []const clap.Param(Id)) type { | 9 | pub fn ComptimeClap(comptime Id: type, comptime params: []const clap.Param(Id)) type { |
| 9 | var flags: usize = 0; | 10 | var flags: usize = 0; |
| @@ -56,11 +57,17 @@ pub fn ComptimeClap(comptime Id: type, comptime params: []const clap.Param(Id)) | |||
| 56 | if (param.names.long == null and param.names.short == null) { | 57 | if (param.names.long == null and param.names.short == null) { |
| 57 | try pos.append(arg.value.?); | 58 | try pos.append(arg.value.?); |
| 58 | } else if (param.takes_value) { | 59 | } else if (param.takes_value) { |
| 59 | // We slice before access to avoid false positive access out of bound | 60 | // If we don't have any optional parameters, then this code should |
| 60 | // compile error. | 61 | // never be reached. |
| 61 | res.options[0..][param.id] = arg.value.?; | 62 | debug.assert(res.options.len != 0); |
| 63 | |||
| 64 | // Hack: Utilize Zigs lazy analyzis to avoid a compiler error | ||
| 65 | if (res.options.len != 0) | ||
| 66 | res.options[param.id] = arg.value.?; | ||
| 62 | } else { | 67 | } else { |
| 63 | res.flags[0..][param.id] = true; | 68 | debug.assert(res.flags.len != 0); |
| 69 | if (res.flags.len != 0) | ||
| 70 | res.flags[param.id] = true; | ||
| 64 | } | 71 | } |
| 65 | } | 72 | } |
| 66 | 73 | ||
| @@ -118,13 +125,13 @@ test "clap.comptime.ComptimeClap" { | |||
| 118 | .names = clap.Names{ | 125 | .names = clap.Names{ |
| 119 | .short = 'a', | 126 | .short = 'a', |
| 120 | .long = "aa", | 127 | .long = "aa", |
| 121 | } | 128 | }, |
| 122 | }, | 129 | }, |
| 123 | clap.Param(void){ | 130 | clap.Param(void){ |
| 124 | .names = clap.Names{ | 131 | .names = clap.Names{ |
| 125 | .short = 'b', | 132 | .short = 'b', |
| 126 | .long = "bb", | 133 | .long = "bb", |
| 127 | } | 134 | }, |
| 128 | }, | 135 | }, |
| 129 | clap.Param(void){ | 136 | clap.Param(void){ |
| 130 | .names = clap.Names{ | 137 | .names = clap.Names{ |
diff --git a/src/streaming.zig b/src/streaming.zig index 9da120c..d23471c 100644 --- a/src/streaming.zig +++ b/src/streaming.zig | |||
| @@ -174,7 +174,7 @@ fn testNoErr(params: []const clap.Param(u8), args_strings: []const []const u8, r | |||
| 174 | var iter = args.SliceIterator{ .args = args_strings }; | 174 | var iter = args.SliceIterator{ .args = args_strings }; |
| 175 | var c = StreamingClap(u8, args.SliceIterator){ | 175 | var c = StreamingClap(u8, args.SliceIterator){ |
| 176 | .params = params, | 176 | .params = params, |
| 177 | .iter = &iter | 177 | .iter = &iter, |
| 178 | }; | 178 | }; |
| 179 | 179 | ||
| 180 | for (results) |res| { | 180 | for (results) |res| { |
| @@ -217,9 +217,9 @@ test "clap.streaming.StreamingClap: short params" { | |||
| 217 | testNoErr( | 217 | testNoErr( |
| 218 | params, | 218 | params, |
| 219 | [_][]const u8{ | 219 | [_][]const u8{ |
| 220 | "-a", "-b", "-ab", "-ba", | 220 | "-a", "-b", "-ab", "-ba", |
| 221 | "-c", "0", "-c=0", "-ac", | 221 | "-c", "0", "-c=0", "-ac", |
| 222 | "0", "-ac=0", | 222 | "0", "-ac=0", |
| 223 | }, | 223 | }, |
| 224 | [_]Arg(u8){ | 224 | [_]Arg(u8){ |
| 225 | Arg(u8){ .param = a }, | 225 | Arg(u8){ .param = a }, |
| @@ -262,8 +262,8 @@ test "clap.streaming.StreamingClap: long params" { | |||
| 262 | testNoErr( | 262 | testNoErr( |
| 263 | params, | 263 | params, |
| 264 | [_][]const u8{ | 264 | [_][]const u8{ |
| 265 | "--aa", "--bb", | 265 | "--aa", "--bb", |
| 266 | "--cc", "0", | 266 | "--cc", "0", |
| 267 | "--cc=0", | 267 | "--cc=0", |
| 268 | }, | 268 | }, |
| 269 | [_]Arg(u8){ | 269 | [_]Arg(u8){ |
| @@ -276,12 +276,10 @@ test "clap.streaming.StreamingClap: long params" { | |||
| 276 | } | 276 | } |
| 277 | 277 | ||
| 278 | test "clap.streaming.StreamingClap: positional params" { | 278 | test "clap.streaming.StreamingClap: positional params" { |
| 279 | const params = [_]clap.Param(u8){ | 279 | const params = [_]clap.Param(u8){clap.Param(u8){ |
| 280 | clap.Param(u8){ | 280 | .id = 0, |
| 281 | .id = 0, | 281 | .takes_value = true, |
| 282 | .takes_value = true, | 282 | }}; |
| 283 | }, | ||
| 284 | }; | ||
| 285 | 283 | ||
| 286 | testNoErr( | 284 | testNoErr( |
| 287 | params, | 285 | params, |
| @@ -331,10 +329,10 @@ test "clap.streaming.StreamingClap: all params" { | |||
| 331 | testNoErr( | 329 | testNoErr( |
| 332 | params, | 330 | params, |
| 333 | [_][]const u8{ | 331 | [_][]const u8{ |
| 334 | "-a", "-b", "-ab", "-ba", | 332 | "-a", "-b", "-ab", "-ba", |
| 335 | "-c", "0", "-c=0", "-ac", | 333 | "-c", "0", "-c=0", "-ac", |
| 336 | "0", "-ac=0", "--aa", "--bb", | 334 | "0", "-ac=0", "--aa", "--bb", |
| 337 | "--cc", "0", "--cc=0", "something", | 335 | "--cc", "0", "--cc=0", "something", |
| 338 | }, | 336 | }, |
| 339 | [_]Arg(u8){ | 337 | [_]Arg(u8){ |
| 340 | Arg(u8){ .param = aa }, | 338 | Arg(u8){ .param = aa }, |