From 661d32ded068ecff486a952626bd209240035781 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Sun, 20 May 2018 12:51:43 +0200 Subject: Support for command params in core.zig * Also refactored structure * related: #1 --- core.zig | 158 +++++++++++++++++++++-------- example.zig | 31 +++--- extended.zig | 274 --------------------------------------------------- index.zig | 317 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 449 insertions(+), 331 deletions(-) delete mode 100644 extended.zig create mode 100644 index.zig diff --git a/core.zig b/core.zig index 8b953a9..33ba59c 100644 --- a/core.zig +++ b/core.zig @@ -18,47 +18,84 @@ const debug = std.debug; /// * "-abc value" /// * "-abc=value" /// * "-abcvalue" -/// * Long ("--long-arg"): Should be used for less common parameters, or when no single character +/// * Long ("--long-param"): Should be used for less common parameters, or when no single character /// can describe the paramter. /// * They can take a value two different ways. -/// * "--long-arg value" -/// * "--long-arg=value" -/// * Value ("some-value"): Should be used as the primary parameter of the program, like a -/// filename or an expression to parse. +/// * "--long-param value" +/// * "--long-param=value" +/// * Command ("command"): Should be used as for sub-commands and other keywords. +/// * They can take a value two different ways. +/// * "command value" +/// * "command=value" +/// * Value ("value"): Should be used as the primary parameter of the program, like a filename or +/// an expression to parse. pub fn Param(comptime Id: type) type { return struct { const Self = this; id: Id, + command: ?[]const u8, short: ?u8, long: ?[]const u8, takes_value: bool, - /// Initialize a ::Param. - /// If ::name.len == 0, then it's a value parameter: "some-command value". - /// If ::name.len == 1, then it's a short parameter: "some-command -s". - /// If ::name.len > 1, then it's a long parameter: "some-command --long". - pub fn init(id: Id, name: []const u8, takes_value: bool) Self { + pub fn short(id: Id, s: u8, takes_value: bool) Self { return Self{ .id = id, - .short = if (name.len == 1) name[0] else null, - .long = if (name.len > 1) name else null, + .command = null, + .short = s, + .long = null, + .takes_value = takes_value, + }; + } + + pub fn long(id: Id, l: []const u8, takes_value: bool) Self { + return Self{ + .id = id, + .command = null, + .short = null, + .long = l, .takes_value = takes_value, }; } - pub fn both(id: Id, short: u8, long: []const u8, takes_value: bool) Self { + pub fn command(id: Id, c: []const u8, takes_value: bool) Self { + return Self{ + .id = id, + .command = c, + .short = null, + .long = null, + .takes_value = takes_value, + }; + } + + pub fn value(id: Id) Self { + return Self{ + .id = id, + .command = null, + .short = null, + .long = null, + .takes_value = true, + }; + } + + /// Initialize a ::Param. + /// If ::name.len == 0, then it's a value parameter: "value". + /// If ::name.len == 1, then it's a short parameter: "-s". + /// If ::name.len > 1, then it's a long parameter: "--long". + pub fn smart(id: Id, name: []const u8, takes_value: bool) Self { return Self{ .id = id, - .short = short, - .long = long, + .command = null, + .short = if (name.len == 1) name[0] else null, + .long = if (name.len > 1) name else null, .takes_value = takes_value, }; } - pub fn with(param: &const Self, comptime field_name: []const u8, value: var) Self { + pub fn with(param: &const Self, comptime field_name: []const u8, v: var) Self { var res = *param; - @field(res, field_name) = value; + @field(res, field_name) = v; return res; } }; @@ -136,7 +173,7 @@ pub const OsArgIterator = struct { if (builtin.os == builtin.Os.windows) { return try self.args.next(allocator) ?? return null; } else { - return self.args.nextPoxix(); + return self.args.nextPosix(); } } }; @@ -180,7 +217,7 @@ pub fn Iterator(comptime Id: type) type { /// Get the next ::Arg that matches a ::Param. pub fn next(iter: &Self) !?Arg(Id) { const ArgInfo = struct { - const Kind = enum { Long, Short, Value }; + const Kind = enum { Long, Short, Command }; arg: []const u8, kind: Kind, @@ -191,7 +228,7 @@ pub fn Iterator(comptime Id: type) type { const full_arg = (try iter.innerNext()) ?? return null; const arg_info = blk: { var arg = full_arg; - var kind = ArgInfo.Kind.Value; + var kind = ArgInfo.Kind.Command; if (mem.startsWith(u8, arg, "--")) { arg = arg[2..]; @@ -213,12 +250,17 @@ pub fn Iterator(comptime Id: type) type { for (iter.params) |*param| { switch (kind) { + ArgInfo.Kind.Command, ArgInfo.Kind.Long => { - const long = param.long ?? continue; + const match = switch (kind) { + ArgInfo.Kind.Command => param.command ?? continue, + ArgInfo.Kind.Long => param.long ?? continue, + else => unreachable, + }; const name = if (eql_index) |i| arg[0..i] else arg; const maybe_value = if (eql_index) |i| arg[i + 1..] else null; - if (!mem.eql(u8, name, long)) + if (!mem.eql(u8, name, match)) continue; if (!param.takes_value) { if (maybe_value != null) @@ -247,12 +289,17 @@ pub fn Iterator(comptime Id: type) type { .param = param, }); }, - ArgInfo.Kind.Value => { - if (param.long) |_| continue; - if (param.short) |_| continue; + } + } - return Arg(Id).init(param.id, arg); - } + // We do a final pass to look for value parameters matches + if (kind == ArgInfo.Kind.Command) { + for (iter.params) |*param| { + if (param.short) |_| continue; + if (param.long) |_| continue; + if (param.command) |_| continue; + + return Arg(Id).init(param.id, arg); } } @@ -327,11 +374,11 @@ fn testNoErr(params: []const Param(u8), args: []const []const u8, ids: []const u } } -test "clap.parse: short" { +test "clap.core: short" { const params = []Param(u8) { - Param(u8).init(0, "a", false), - Param(u8).init(1, "b", false), - Param(u8).init(2, "c", true), + Param(u8).smart(0, "a", false), + Param(u8).smart(1, "b", false), + Param(u8).smart(2, "c", true), }; testNoErr(params, [][]const u8 { "-a" }, []u8{0}, []?[]const u8{null}); @@ -345,11 +392,11 @@ test "clap.parse: short" { testNoErr(params, [][]const u8 { "-abc100" }, []u8{0,1,2}, []?[]const u8{null,null,"100"}); } -test "clap.parse: long" { +test "clap.core: long" { const params = []Param(u8) { - Param(u8).init(0, "aa", false), - Param(u8).init(1, "bb", false), - Param(u8).init(2, "cc", true), + Param(u8).smart(0, "aa", false), + Param(u8).smart(1, "bb", false), + Param(u8).smart(2, "cc", true), }; testNoErr(params, [][]const u8 { "--aa" }, []u8{0}, []?[]const u8{null}); @@ -358,11 +405,39 @@ test "clap.parse: long" { testNoErr(params, [][]const u8 { "--cc", "100" }, []u8{2}, []?[]const u8{"100"}); } -test "clap.parse: both" { +test "clap.core: command" { + const params = []Param(u8) { + Param(u8).command(0, "aa", false), + Param(u8).command(1, "bb", false), + Param(u8).command(2, "cc", true), + }; + + testNoErr(params, [][]const u8 { "aa" }, []u8{0}, []?[]const u8{null}); + testNoErr(params, [][]const u8 { "aa", "bb" }, []u8{0,1}, []?[]const u8{null,null}); + testNoErr(params, [][]const u8 { "cc=100" }, []u8{2}, []?[]const u8{"100"}); + testNoErr(params, [][]const u8 { "cc", "100" }, []u8{2}, []?[]const u8{"100"}); +} + +test "clap.core: value" { + const params = []Param(u8) { + Param(u8).value(0), + }; + + testNoErr(params, [][]const u8 { "aa" }, []u8{0}, []?[]const u8{"aa"}); +} + +test "clap.core: all" { const params = []Param(u8) { - Param(u8).both(0, 'a', "aa", false), - Param(u8).both(1, 'b', "bb", false), - Param(u8).both(2, 'c', "cc", true), + Param(u8).short(0, 'a', false) + .with("long", "aa"[0..]) + .with("command", "aa"[0..]), + Param(u8).short(1, 'b', false) + .with("long", "bb"[0..]) + .with("command", "bb"[0..]), + Param(u8).short(2, 'c', true) + .with("long", "cc"[0..]) + .with("command", "cc"[0..]), + Param(u8).value(3), }; testNoErr(params, [][]const u8 { "-a" }, []u8{0}, []?[]const u8{null}); @@ -378,4 +453,9 @@ test "clap.parse: both" { testNoErr(params, [][]const u8 { "--aa", "--bb" }, []u8{0,1}, []?[]const u8{null,null}); testNoErr(params, [][]const u8 { "--cc=100" }, []u8{2}, []?[]const u8{"100"}); testNoErr(params, [][]const u8 { "--cc", "100" }, []u8{2}, []?[]const u8{"100"}); + testNoErr(params, [][]const u8 { "aa" }, []u8{0}, []?[]const u8{null}); + testNoErr(params, [][]const u8 { "aa", "bb" }, []u8{0,1}, []?[]const u8{null,null}); + testNoErr(params, [][]const u8 { "cc=100" }, []u8{2}, []?[]const u8{"100"}); + testNoErr(params, [][]const u8 { "cc", "100" }, []u8{2}, []?[]const u8{"100"}); + testNoErr(params, [][]const u8 { "dd" }, []u8{3}, []?[]const u8{"dd"}); } diff --git a/example.zig b/example.zig index 14b5487..4b3fa82 100644 --- a/example.zig +++ b/example.zig @@ -1,14 +1,9 @@ const std = @import("std"); -const core = @import("core.zig"); -const clap = @import("extended.zig"); +const clap = @import("index.zig"); const debug = std.debug; const os = std.os; -const Clap = clap.Clap; -const Param = clap.Param; -const Parser = clap.Parser; - const Options = struct { print_values: bool, a: i64, @@ -35,7 +30,7 @@ const Options = struct { // d = V=5 pub fn main() !void { - const parser = comptime Clap(Options) { + const parser = comptime clap.Clap(Options) { .defaults = Options { .print_values = false, .a = 0, @@ -43,22 +38,22 @@ pub fn main() !void { .c = 0, .d = "", }, - .params = []Param { - Param.init("a") - .with("takes_value", Parser.int(i64, 10)), - Param.init("b") - .with("takes_value", Parser.int(u64, 10)), - Param.init("c") - .with("takes_value", Parser.int(u8, 10)), - Param.init("d") - .with("takes_value", Parser.string), - Param.init("print_values") + .params = []clap.Param { + clap.Param.smart("a") + .with("takes_value", clap.Parser.int(i64, 10)), + clap.Param.smart("b") + .with("takes_value", clap.Parser.int(u64, 10)), + clap.Param.smart("c") + .with("takes_value", clap.Parser.int(u8, 10)), + clap.Param.smart("d") + .with("takes_value", clap.Parser.string), + clap.Param.smart("print_values") .with("short", 'p') .with("long", "print-values"), } }; - var arg_iter = core.OsArgIterator.init(); + var arg_iter = clap.core.OsArgIterator.init(); const iter = &arg_iter.iter; const command = iter.next(debug.global_allocator); diff --git a/extended.zig b/extended.zig deleted file mode 100644 index a5c8e89..0000000 --- a/extended.zig +++ /dev/null @@ -1,274 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); -const core = @import("core.zig"); - -const mem = std.mem; -const fmt = std.fmt; -const debug = std.debug; -const io = std.io; - -const assert = debug.assert; - -pub const Param = struct { - field: []const u8, - short: ?u8, - long: ?[]const u8, - takes_value: ?Parser, - required: bool, - position: ?usize, - - pub fn init(name: []const u8) Param { - return Param { - .field = name, - .short = if (name.len == 1) name[0] else null, - .long = if (name.len > 1) name else null, - .takes_value = null, - .required = false, - .position = null, - }; - } - - pub fn with(param: &const Param, comptime field_name: []const u8, value: var) Param { - var res = *param; - @field(res, field_name) = value; - return res; - } -}; - -pub fn Clap(comptime Result: type) type { - return struct { - const Self = this; - - defaults: Result, - params: []const Param, - - pub fn parse(comptime clap: &const Self, allocator: &mem.Allocator, arg_iter: &core.ArgIterator) !Result { - var result = clap.defaults; - const core_params = comptime blk: { - var res: [clap.params.len]core.Param(usize) = undefined; - - for (clap.params) |p, i| { - res[i] = core.Param(usize) { - .id = i, - .short = p.short, - .long = p.long, - .takes_value = p.takes_value != null, - }; - } - - break :blk res; - }; - - var handled = comptime blk: { - var res: [clap.params.len]bool = undefined; - for (clap.params) |p, i| { - res[i] = !p.required; - } - - break :blk res; - }; - - var pos: usize = 0; - var iter = core.Iterator(usize).init(core_params, arg_iter, allocator); - defer iter.deinit(); - while (try iter.next()) |arg| : (pos += 1) { - inline for(clap.params) |param, i| { - if (arg.id == i) { - if (param.position) |expected| { - if (expected != pos) - return error.InvalidPosition; - } - - if (param.takes_value) |parser| { - try parser.parse(&@field(result, param.field), ??arg.value); - } else { - @field(result, param.field) = true; - } - handled[i] = true; - } - } - } - - return result; - } - }; -} - -pub const Parser = struct { - const UnsafeFunction = &const void; - - FieldType: type, - Errors: type, - func: UnsafeFunction, - - pub fn init(comptime FieldType: type, comptime Errors: type, func: parseFunc(FieldType, Errors)) Parser { - return Parser { - .FieldType = FieldType, - .Errors = Errors, - .func = @ptrCast(UnsafeFunction, func), - }; - } - - fn parse(comptime parser: Parser, field_ptr: takePtr(parser.FieldType), arg: []const u8) parser.Errors!void { - return @ptrCast(parseFunc(parser.FieldType, parser.Errors), parser.func)(field_ptr, arg); - } - - // TODO: This is a workaround, since we don't have pointer reform yet. - fn takePtr(comptime T: type) type { return &T; } - - fn parseFunc(comptime FieldType: type, comptime Errors: type) type { - return fn(&FieldType, []const u8) Errors!void; - } - - pub fn int(comptime Int: type, comptime radix: u8) Parser { - const func = struct { - fn i(field_ptr: &Int, arg: []const u8) !void { - *field_ptr = try fmt.parseInt(Int, arg, radix); - } - }.i; - return Parser.init( - Int, - @typeOf(func).ReturnType.ErrorSet, - func - ); - } - - const string = Parser.init( - []const u8, - error{}, - struct { - fn s(field_ptr: &[]const u8, arg: []const u8) (error{}!void) { - *field_ptr = arg; - } - }.s - ); -}; - - -const Options = struct { - str: []const u8, - int: i64, - uint: u64, - a: bool, - b: bool, - cc: bool, - - pub fn with(op: &const Options, comptime field: []const u8, value: var) Options { - var res = *op; - @field(res, field) = value; - return res; - } -}; - -const default = Options { - .str = "", - .int = 0, - .uint = 0, - .a = false, - .b = false, - .cc = false, -}; - -fn testNoErr(comptime clap: &const Clap(Options), args: []const []const u8, expected: &const Options) void { - var arg_iter = core.ArgSliceIterator.init(args); - const actual = clap.parse(debug.global_allocator, &arg_iter.iter) catch unreachable; - assert(mem.eql(u8, expected.str, actual.str)); - assert(expected.int == actual.int); - assert(expected.uint == actual.uint); - assert(expected.a == actual.a); - assert(expected.b == actual.b); - assert(expected.cc == actual.cc); -} - -fn testErr(comptime clap: &const Clap(Options), args: []const []const u8, expected: error) void { - var arg_iter = core.ArgSliceIterator.init(args); - if (clap.parse(debug.global_allocator, &arg_iter.iter)) |actual| { - unreachable; - } else |err| { - assert(err == expected); - } -} - -test "clap.parse: short" { - const clap = comptime Clap(Options) { - .defaults = default, - .params = []Param { - Param.init("a"), - Param.init("b"), - Param.init("int") - .with("short", 'i') - .with("takes_value", Parser.int(i64, 10)) - } - }; - - testNoErr(clap, [][]const u8 { "-a" }, default.with("a", true)); - testNoErr(clap, [][]const u8 { "-a", "-b" }, default.with("a", true).with("b", true)); - testNoErr(clap, [][]const u8 { "-i=100" }, default.with("int", 100)); - testNoErr(clap, [][]const u8 { "-i100" }, default.with("int", 100)); - testNoErr(clap, [][]const u8 { "-i", "100" }, default.with("int", 100)); - testNoErr(clap, [][]const u8 { "-ab" }, default.with("a", true).with("b", true)); - testNoErr(clap, [][]const u8 { "-abi", "100" }, default.with("a", true).with("b", true).with("int", 100)); - testNoErr(clap, [][]const u8 { "-abi=100" }, default.with("a", true).with("b", true).with("int", 100)); - testNoErr(clap, [][]const u8 { "-abi100" }, default.with("a", true).with("b", true).with("int", 100)); -} - -test "clap.parse: long" { - const clap = comptime Clap(Options) { - .defaults = default, - .params = []Param { - Param.init("cc"), - Param.init("int").with("takes_value", Parser.int(i64, 10)), - Param.init("uint").with("takes_value", Parser.int(u64, 10)), - Param.init("str").with("takes_value", Parser.string), - } - }; - - testNoErr(clap, [][]const u8 { "--cc" }, default.with("cc", true)); - testNoErr(clap, [][]const u8 { "--int", "100" }, default.with("int", 100)); -} - -test "clap.parse: value bool" { - const clap = comptime Clap(Options) { - .defaults = default, - .params = []Param { - Param.init("a"), - } - }; - - testNoErr(clap, [][]const u8 { "-a" }, default.with("a", true)); -} - -test "clap.parse: value str" { - const clap = comptime Clap(Options) { - .defaults = default, - .params = []Param { - Param.init("str").with("takes_value", Parser.string), - } - }; - - testNoErr(clap, [][]const u8 { "--str", "Hello World!" }, default.with("str", "Hello World!")); -} - -test "clap.parse: value int" { - const clap = comptime Clap(Options) { - .defaults = default, - .params = []Param { - Param.init("int").with("takes_value", Parser.int(i64, 10)), - } - }; - - testNoErr(clap, [][]const u8 { "--int", "100" }, default.with("int", 100)); -} - -test "clap.parse: position" { - const clap = comptime Clap(Options) { - .defaults = default, - .params = []Param { - Param.init("a").with("position", 0), - Param.init("b").with("position", 1), - } - }; - - testNoErr(clap, [][]const u8 { "-a", "-b" }, default.with("a", true).with("b", true)); - testErr(clap, [][]const u8 { "-b", "-a" }, error.InvalidPosition); -} diff --git a/index.zig b/index.zig new file mode 100644 index 0000000..7b82211 --- /dev/null +++ b/index.zig @@ -0,0 +1,317 @@ +pub const core = @import("core.zig"); + +const builtin = @import("builtin"); +const std = @import("std"); + +const mem = std.mem; +const fmt = std.fmt; +const debug = std.debug; +const io = std.io; + +const assert = debug.assert; + +pub const Param = struct { + field: []const u8, + short: ?u8, + long: ?[]const u8, + takes_value: ?Parser, + required: bool, + position: ?usize, + + pub fn short(s: u8) Param { + return Param{ + .field = []u8{s}, + .short = s, + .long = null, + .takes_value = null, + .required = false, + .position = null, + }; + } + + pub fn long(l: []const u8) Param { + return Param{ + .field = l, + .short = null, + .long = l, + .takes_value = null, + .required = false, + .position = null, + }; + } + + pub fn value(f: []const u8) Param { + return Param{ + .field = f, + .short = null, + .long = null, + .takes_value = null, + .required = false, + .position = null, + }; + } + + /// Initialize a ::Param. + /// If ::name.len == 0, then it's a value parameter: "value". + /// If ::name.len == 1, then it's a short parameter: "-s". + /// If ::name.len > 1, then it's a long parameter: "--long". + pub fn smart(name: []const u8) Param { + return Param{ + .field = name, + .short = if (name.len == 1) name[0] else null, + .long = if (name.len > 1) name else null, + .takes_value = null, + .required = false, + .position = null, + }; + } + + pub fn with(param: &const Param, comptime field_name: []const u8, v: var) Param { + var res = *param; + @field(res, field_name) = v; + return res; + } +}; + +pub fn Clap(comptime Result: type) type { + return struct { + const Self = this; + + defaults: Result, + params: []const Param, + + pub fn parse(comptime clap: &const Self, allocator: &mem.Allocator, arg_iter: &core.ArgIterator) !Result { + var result = clap.defaults; + const core_params = comptime blk: { + var res: [clap.params.len]core.Param(usize) = undefined; + + for (clap.params) |p, i| { + res[i] = core.Param(usize) { + .id = i, + .command = null, + .short = p.short, + .long = p.long, + .takes_value = p.takes_value != null, + }; + } + + break :blk res; + }; + + var handled = comptime blk: { + var res: [clap.params.len]bool = undefined; + for (clap.params) |p, i| { + res[i] = !p.required; + } + + break :blk res; + }; + + var pos: usize = 0; + var iter = core.Iterator(usize).init(core_params, arg_iter, allocator); + defer iter.deinit(); + while (try iter.next()) |arg| : (pos += 1) { + inline for(clap.params) |param, i| { + if (arg.id == i) { + if (param.position) |expected| { + if (expected != pos) + return error.InvalidPosition; + } + + if (param.takes_value) |parser| { + try parser.parse(&@field(result, param.field), ??arg.value); + } else { + @field(result, param.field) = true; + } + handled[i] = true; + } + } + } + + return result; + } + }; +} + +pub const Parser = struct { + const UnsafeFunction = &const void; + + FieldType: type, + Errors: type, + func: UnsafeFunction, + + pub fn init(comptime FieldType: type, comptime Errors: type, func: parseFunc(FieldType, Errors)) Parser { + return Parser { + .FieldType = FieldType, + .Errors = Errors, + .func = @ptrCast(UnsafeFunction, func), + }; + } + + fn parse(comptime parser: Parser, field_ptr: takePtr(parser.FieldType), arg: []const u8) parser.Errors!void { + return @ptrCast(parseFunc(parser.FieldType, parser.Errors), parser.func)(field_ptr, arg); + } + + // TODO: This is a workaround, since we don't have pointer reform yet. + fn takePtr(comptime T: type) type { return &T; } + + fn parseFunc(comptime FieldType: type, comptime Errors: type) type { + return fn(&FieldType, []const u8) Errors!void; + } + + pub fn int(comptime Int: type, comptime radix: u8) Parser { + const func = struct { + fn i(field_ptr: &Int, arg: []const u8) !void { + *field_ptr = try fmt.parseInt(Int, arg, radix); + } + }.i; + return Parser.init( + Int, + @typeOf(func).ReturnType.ErrorSet, + func + ); + } + + const string = Parser.init( + []const u8, + error{}, + struct { + fn s(field_ptr: &[]const u8, arg: []const u8) (error{}!void) { + *field_ptr = arg; + } + }.s + ); +}; + + +const Options = struct { + str: []const u8, + int: i64, + uint: u64, + a: bool, + b: bool, + cc: bool, + + pub fn with(op: &const Options, comptime field: []const u8, value: var) Options { + var res = *op; + @field(res, field) = value; + return res; + } +}; + +const default = Options { + .str = "", + .int = 0, + .uint = 0, + .a = false, + .b = false, + .cc = false, +}; + +fn testNoErr(comptime clap: &const Clap(Options), args: []const []const u8, expected: &const Options) void { + var arg_iter = core.ArgSliceIterator.init(args); + const actual = clap.parse(debug.global_allocator, &arg_iter.iter) catch unreachable; + assert(mem.eql(u8, expected.str, actual.str)); + assert(expected.int == actual.int); + assert(expected.uint == actual.uint); + assert(expected.a == actual.a); + assert(expected.b == actual.b); + assert(expected.cc == actual.cc); +} + +fn testErr(comptime clap: &const Clap(Options), args: []const []const u8, expected: error) void { + var arg_iter = core.ArgSliceIterator.init(args); + if (clap.parse(debug.global_allocator, &arg_iter.iter)) |actual| { + unreachable; + } else |err| { + assert(err == expected); + } +} + +test "clap.core" { + _ = core; +} + +test "clap: short" { + const clap = comptime Clap(Options) { + .defaults = default, + .params = []Param { + Param.smart("a"), + Param.smart("b"), + Param.smart("int") + .with("short", 'i') + .with("takes_value", Parser.int(i64, 10)) + } + }; + + testNoErr(clap, [][]const u8 { "-a" }, default.with("a", true)); + testNoErr(clap, [][]const u8 { "-a", "-b" }, default.with("a", true).with("b", true)); + testNoErr(clap, [][]const u8 { "-i=100" }, default.with("int", 100)); + testNoErr(clap, [][]const u8 { "-i100" }, default.with("int", 100)); + testNoErr(clap, [][]const u8 { "-i", "100" }, default.with("int", 100)); + testNoErr(clap, [][]const u8 { "-ab" }, default.with("a", true).with("b", true)); + testNoErr(clap, [][]const u8 { "-abi", "100" }, default.with("a", true).with("b", true).with("int", 100)); + testNoErr(clap, [][]const u8 { "-abi=100" }, default.with("a", true).with("b", true).with("int", 100)); + testNoErr(clap, [][]const u8 { "-abi100" }, default.with("a", true).with("b", true).with("int", 100)); +} + +test "clap: long" { + const clap = comptime Clap(Options) { + .defaults = default, + .params = []Param { + Param.smart("cc"), + Param.smart("int").with("takes_value", Parser.int(i64, 10)), + Param.smart("uint").with("takes_value", Parser.int(u64, 10)), + Param.smart("str").with("takes_value", Parser.string), + } + }; + + testNoErr(clap, [][]const u8 { "--cc" }, default.with("cc", true)); + testNoErr(clap, [][]const u8 { "--int", "100" }, default.with("int", 100)); +} + +test "clap: value bool" { + const clap = comptime Clap(Options) { + .defaults = default, + .params = []Param { + Param.smart("a"), + } + }; + + testNoErr(clap, [][]const u8 { "-a" }, default.with("a", true)); +} + +test "clap: value str" { + const clap = comptime Clap(Options) { + .defaults = default, + .params = []Param { + Param.smart("str").with("takes_value", Parser.string), + } + }; + + testNoErr(clap, [][]const u8 { "--str", "Hello World!" }, default.with("str", "Hello World!")); +} + +test "clap: value int" { + const clap = comptime Clap(Options) { + .defaults = default, + .params = []Param { + Param.smart("int").with("takes_value", Parser.int(i64, 10)), + } + }; + + testNoErr(clap, [][]const u8 { "--int", "100" }, default.with("int", 100)); +} + +test "clap: position" { + const clap = comptime Clap(Options) { + .defaults = default, + .params = []Param { + Param.smart("a").with("position", 0), + Param.smart("b").with("position", 1), + } + }; + + testNoErr(clap, [][]const u8 { "-a", "-b" }, default.with("a", true).with("b", true)); + testErr(clap, [][]const u8 { "-b", "-a" }, error.InvalidPosition); +} -- cgit v1.2.3