From a305e818bd986c599fff17141617bc4f890276cf Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Wed, 27 Nov 2019 23:13:31 +0900 Subject: Add clap.parse as the simplest way of using the lib --- README.md | 160 ++++++++++++++++++++++++---------------- build.zig | 8 +- clap.zig | 41 ++++++++++ example/README.md.template | 50 ++++++++----- example/comptime-clap-error.zig | 19 ----- example/simple-error.zig | 15 ++++ example/simple.zig | 26 +++++++ 7 files changed, 213 insertions(+), 106 deletions(-) delete mode 100644 example/comptime-clap-error.zig create mode 100644 example/simple-error.zig create mode 100644 example/simple.zig diff --git a/README.md b/README.md index 6c556b9..0555a0f 100644 --- a/README.md +++ b/README.md @@ -15,10 +15,9 @@ A simple and easy to use command line argument parser library for Zig. ## Examples -### `StreamingClap` +### `clap.parse` -The `StreamingClap` is the base of all the other parsers. It's a streaming parser that uses an -`args.Iterator` to provide it with arguments lazily. +The simplest way to use this library is to just call the `clap.parse` function. ```zig const std = @import("std"); @@ -27,58 +26,69 @@ const clap = @import("clap"); const debug = std.debug; pub fn main() !void { - const allocator = std.heap.direct_allocator; - // First we specify what parameters our program can take. - const params = [_]clap.Param(u8){ - clap.Param(u8){ - .id = 'h', - .names = clap.Names{ .short = 'h', .long = "help" }, - }, - clap.Param(u8){ - .id = 'n', - .names = clap.Names{ .short = 'n', .long = "number" }, - .takes_value = true, - }, - clap.Param(u8){ - .id = 'f', + // We can use `parseParam` to parse a string to a `Param(Help)` + const params = comptime [_]clap.Param(clap.Help){ + clap.parseParam("-h, --help Display this help and exit. ") catch unreachable, + clap.parseParam("-n, --number An option parameter, which takes a value.") catch unreachable, + clap.Param(clap.Help){ .takes_value = true, }, }; - // We then initialize an argument iterator. We will use the OsIterator as it nicely - // wraps iterating over arguments the most efficient way on each os. - var iter = try clap.args.OsIterator.init(allocator); - defer iter.deinit(); + var args = try clap.parse(clap.Help, params, std.heap.direct_allocator); + defer args.deinit(); - // Initialize our streaming parser. - var parser = clap.StreamingClap(u8, clap.args.OsIterator){ - .params = params, - .iter = &iter, + if (args.flag("--help")) + debug.warn("--help\n"); + if (args.option("--number")) |n| + debug.warn("--number = {}\n", n); + for (args.positionals()) |pos| + debug.warn("{}\n", pos); +} + +``` + +The data structure returned has lookup speed on par with array access (`arr[i]`) and validates +that the strings you pass to `option` and `flag` are actually parameters that the program can take: + +```zig +const std = @import("std"); +const clap = @import("clap"); + +pub fn main() !void { + // First we specify what parameters our program can take. + // We can use `parseParam` to parse a string to a `Param(Help)` + const params = comptime [_]clap.Param(clap.Help){ + clap.parseParam("-h, --help Display this help and exit.") catch unreachable, }; - // Because we use a streaming parser, we have to consume each argument parsed individually. - while (try parser.next()) |arg| { - // arg.param will point to the parameter which matched the argument. - switch (arg.param.id) { - 'h' => debug.warn("Help!\n"), - 'n' => debug.warn("--number = {}\n", arg.value.?), + var args = try clap.parse(clap.Help, params, std.heap.direct_allocator); + defer args.deinit(); - // arg.value == null, if arg.param.takes_value == false. - // Otherwise, arg.value is the value passed with the argument, such as "-a=10" - // or "-a 10". - 'f' => debug.warn("{}\n", arg.value.?), - else => unreachable, - } - } + _ = args.flag("--helps"); } ``` +``` +zig-clap/clap/comptime.zig:109:17: error: --helps is not a parameter. + @compileError(name ++ " is not a parameter."); + ^ +zig-clap/clap/comptime.zig:77:45: note: called from here + const param = comptime findParam(name); + ^ +zig-clap/clap.zig:238:31: note: called from here + return a.clap.flag(name); + ^ +zig-clap/example/simple-error.zig:16:18: note: called from here + _ = args.flag("--helps"); +``` + ### `ComptimeClap` -The `ComptimeClap` is a wrapper for `StreamingClap`, which parses all the arguments and makes -them available through three functions (`flag`, `option`, `positionals`). +The `ComptimeClap` is the parser used by `clap.parse`. It allows the user to use a custom argument +iterator. ```zig const std = @import("std"); @@ -118,46 +128,68 @@ pub fn main() !void { ``` -The data structure returned from this parser has lookup speed on par with array access (`arr[i]`) -and validates that the strings you pass to `option` and `flag` are actually parameters that the -program can take: +### `StreamingClap` + +The `StreamingClap` is the base of all the other parsers. It's a streaming parser that uses an +`args.Iterator` to provide it with arguments lazily. ```zig const std = @import("std"); const clap = @import("clap"); +const debug = std.debug; + pub fn main() !void { const allocator = std.heap.direct_allocator; - const params = [_]clap.Param(void){clap.Param(void){ - .names = clap.Names{ .short = 'h', .long = "help" }, - }}; + // First we specify what parameters our program can take. + const params = [_]clap.Param(u8){ + clap.Param(u8){ + .id = 'h', + .names = clap.Names{ .short = 'h', .long = "help" }, + }, + clap.Param(u8){ + .id = 'n', + .names = clap.Names{ .short = 'n', .long = "number" }, + .takes_value = true, + }, + clap.Param(u8){ + .id = 'f', + .takes_value = true, + }, + }; - var iter = clap.args.OsIterator.init(allocator); + // We then initialize an argument iterator. We will use the OsIterator as it nicely + // wraps iterating over arguments the most efficient way on each os. + var iter = try clap.args.OsIterator.init(allocator); defer iter.deinit(); - const exe = try iter.next(); - var args = try clap.ComptimeClap(void, params).parse(allocator, clap.args.OsIterator, &iter); - defer args.deinit(); + // Initialize our streaming parser. + var parser = clap.StreamingClap(u8, clap.args.OsIterator){ + .params = params, + .iter = &iter, + }; - _ = args.flag("--helps"); -} + // Because we use a streaming parser, we have to consume each argument parsed individually. + while (try parser.next()) |arg| { + // arg.param will point to the parameter which matched the argument. + switch (arg.param.id) { + 'h' => debug.warn("Help!\n"), + 'n' => debug.warn("--number = {}\n", arg.value.?), -``` + // arg.value == null, if arg.param.takes_value == false. + // Otherwise, arg.value is the value passed with the argument, such as "-a=10" + // or "-a 10". + 'f' => debug.warn("{}\n", arg.value.?), + else => unreachable, + } + } +} ``` -zig-clap/src/comptime.zig:109:17: error: --helps is not a parameter. - @compileError(name ++ " is not a parameter."); - ^ -zig-clap/src/comptime.zig:77:45: note: called from here - const param = comptime findParam(name); - ^ -zig-clap/example/comptime-clap-error.zig:18:18: note: called from here - _ = args.flag("--helps"); - ^ -``` -Ofc, this limits you to parameters that are comptime known. +Currently, this parse is the only parser that allow an array of `Param` that +is generated at runtime. ### `help` diff --git a/build.zig b/build.zig index 9d2ef7b..1a6fe09 100644 --- a/build.zig +++ b/build.zig @@ -27,8 +27,9 @@ pub fn build(b: *Builder) void { const example_step = b.step("examples", "Build examples"); inline for ([_][]const u8{ + "simple", + //"simple-error", "comptime-clap", - //"comptime-clap-error", "streaming-clap", "help", }) |example_name| { @@ -61,9 +62,10 @@ fn readMeStep(b: *Builder) *std.build.Step { const stream = &file.outStream().stream; try stream.print( @embedFile("example/README.md.template"), - @embedFile("example/streaming-clap.zig"), + @embedFile("example/simple.zig"), + @embedFile("example/simple-error.zig"), @embedFile("example/comptime-clap.zig"), - @embedFile("example/comptime-clap-error.zig"), + @embedFile("example/streaming-clap.zig"), @embedFile("example/help.zig"), ); } diff --git a/clap.zig b/clap.zig index 1a6a95d..e0d20e3 100644 --- a/clap.zig +++ b/clap.zig @@ -223,6 +223,47 @@ fn find(str: []const u8, f: []const u8) []const u8 { return str[i..][0..f.len]; } +pub fn Args(comptime Id: type, comptime params: []const Param(Id)) type { + return struct { + arena: std.heap.ArenaAllocator, + clap: ComptimeClap(Id, params), + exe_arg: ?[]const u8, + + pub fn deinit(a: *@This()) void { + a.clap.deinit(); + a.arena.deinit(); + } + + pub fn flag(a: @This(), comptime name: []const u8) bool { + return a.clap.flag(name); + } + + pub fn option(a: @This(), comptime name: []const u8) ?[]const u8 { + return a.clap.option(name); + } + + pub fn positionals(a: @This()) []const []const u8 { + return a.clap.positionals(); + } + }; +} + +/// Parses the command line arguments passed into the program based on an +/// array of `Param`s. +pub fn parse( + comptime Id: type, + comptime params: []const Param(Id), + allocator: *mem.Allocator, +) !Args(Id, params) { + var iter = try args.OsIterator.init(allocator); + const clap = try ComptimeClap(Id, params).parse(allocator, args.OsIterator, &iter); + return Args(Id, params){ + .arena = iter.arena, + .clap = clap, + .exe_arg = iter.exe_arg, + }; +} + /// Will print a help message in the following format: /// -s, --long help_text /// -s, help_text diff --git a/example/README.md.template b/example/README.md.template index 373a037..1fd90b0 100644 --- a/example/README.md.template +++ b/example/README.md.template @@ -15,45 +15,55 @@ A simple and easy to use command line argument parser library for Zig. ## Examples -### `StreamingClap` +### `clap.parse` -The `StreamingClap` is the base of all the other parsers. It's a streaming parser that uses an -`args.Iterator` to provide it with arguments lazily. +The simplest way to use this library is to just call the `clap.parse` function. ```zig {} ``` -### `ComptimeClap` - -The `ComptimeClap` is a wrapper for `StreamingClap`, which parses all the arguments and makes -them available through three functions (`flag`, `option`, `positionals`). - -```zig -{} -``` - -The data structure returned from this parser has lookup speed on par with array access (`arr[i]`) -and validates that the strings you pass to `option` and `flag` are actually parameters that the -program can take: +The data structure returned has lookup speed on par with array access (`arr[i]`) and validates +that the strings you pass to `option` and `flag` are actually parameters that the program can take: ```zig {} ``` ``` -zig-clap/src/comptime.zig:109:17: error: --helps is not a parameter. +zig-clap/clap/comptime.zig:109:17: error: --helps is not a parameter. @compileError(name ++ " is not a parameter."); ^ -zig-clap/src/comptime.zig:77:45: note: called from here +zig-clap/clap/comptime.zig:77:45: note: called from here const param = comptime findParam(name); ^ -zig-clap/example/comptime-clap-error.zig:18:18: note: called from here +zig-clap/clap.zig:238:31: note: called from here + return a.clap.flag(name); + ^ +zig-clap/example/simple-error.zig:16:18: note: called from here _ = args.flag("--helps"); - ^ ``` -Ofc, this limits you to parameters that are comptime known. +### `ComptimeClap` + +The `ComptimeClap` is the parser used by `clap.parse`. It allows the user to use a custom argument +iterator. + +```zig +{} +``` + +### `StreamingClap` + +The `StreamingClap` is the base of all the other parsers. It's a streaming parser that uses an +`args.Iterator` to provide it with arguments lazily. + +```zig +{} +``` + +Currently, this parse is the only parser that allow an array of `Param` that +is generated at runtime. ### `help` diff --git a/example/comptime-clap-error.zig b/example/comptime-clap-error.zig deleted file mode 100644 index 3209b74..0000000 --- a/example/comptime-clap-error.zig +++ /dev/null @@ -1,19 +0,0 @@ -const std = @import("std"); -const clap = @import("clap"); - -pub fn main() !void { - const allocator = std.heap.direct_allocator; - - const params = [_]clap.Param(void){clap.Param(void){ - .names = clap.Names{ .short = 'h', .long = "help" }, - }}; - - var iter = clap.args.OsIterator.init(allocator); - defer iter.deinit(); - const exe = try iter.next(); - - var args = try clap.ComptimeClap(void, params).parse(allocator, clap.args.OsIterator, &iter); - defer args.deinit(); - - _ = args.flag("--helps"); -} diff --git a/example/simple-error.zig b/example/simple-error.zig new file mode 100644 index 0000000..fc72a03 --- /dev/null +++ b/example/simple-error.zig @@ -0,0 +1,15 @@ +const std = @import("std"); +const clap = @import("clap"); + +pub fn main() !void { + // First we specify what parameters our program can take. + // We can use `parseParam` to parse a string to a `Param(Help)` + const params = comptime [_]clap.Param(clap.Help){ + clap.parseParam("-h, --help Display this help and exit.") catch unreachable, + }; + + var args = try clap.parse(clap.Help, params, std.heap.direct_allocator); + defer args.deinit(); + + _ = args.flag("--helps"); +} diff --git a/example/simple.zig b/example/simple.zig new file mode 100644 index 0000000..f791447 --- /dev/null +++ b/example/simple.zig @@ -0,0 +1,26 @@ +const std = @import("std"); +const clap = @import("clap"); + +const debug = std.debug; + +pub fn main() !void { + // First we specify what parameters our program can take. + // We can use `parseParam` to parse a string to a `Param(Help)` + const params = comptime [_]clap.Param(clap.Help){ + clap.parseParam("-h, --help Display this help and exit. ") catch unreachable, + clap.parseParam("-n, --number An option parameter, which takes a value.") catch unreachable, + clap.Param(clap.Help){ + .takes_value = true, + }, + }; + + var args = try clap.parse(clap.Help, params, std.heap.direct_allocator); + defer args.deinit(); + + if (args.flag("--help")) + debug.warn("--help\n"); + if (args.option("--number")) |n| + debug.warn("--number = {}\n", n); + for (args.positionals()) |pos| + debug.warn("{}\n", pos); +} -- cgit v1.2.3