summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--build.zig24
-rw-r--r--clap.zig87
2 files changed, 65 insertions, 46 deletions
diff --git a/build.zig b/build.zig
index 0d253e2..03834b4 100644
--- a/build.zig
+++ b/build.zig
@@ -7,17 +7,23 @@ pub fn build(b: *Builder) void {
7 const target = b.standardTargetOptions(.{}); 7 const target = b.standardTargetOptions(.{});
8 8
9 const test_all_step = b.step("test", "Run all tests in all modes."); 9 const test_all_step = b.step("test", "Run all tests in all modes.");
10 inline for (@typeInfo(std.builtin.Mode).Enum.fields) |field| { 10 for ([_]bool{ true, false }) |stage1| {
11 const test_mode = @field(std.builtin.Mode, field.name); 11 for (std.meta.tags(std.builtin.Mode)) |test_mode| {
12 const mode_str = @tagName(test_mode); 12 const mode_str = @tagName(test_mode);
13 const stage1_str = if (stage1) "stage1" else "stage2";
13 14
14 const tests = b.addTest("clap.zig"); 15 const tests = b.addTest("clap.zig");
15 tests.setBuildMode(test_mode); 16 tests.setBuildMode(test_mode);
16 tests.setTarget(target); 17 tests.setTarget(target);
18 tests.use_stage1 = stage1;
17 19
18 const test_step = b.step("test-" ++ mode_str, "Run all tests in " ++ mode_str ++ "."); 20 const test_step = b.step(
19 test_step.dependOn(&tests.step); 21 b.fmt("test-{s}-{s}", .{ stage1_str, mode_str }),
20 test_all_step.dependOn(test_step); 22 b.fmt("Run all tests with {s} compiler in {s}.", .{ stage1_str, mode_str }),
23 );
24 test_step.dependOn(&tests.step);
25 test_all_step.dependOn(test_step);
26 }
21 } 27 }
22 28
23 const example_step = b.step("examples", "Build examples"); 29 const example_step = b.step("examples", "Build examples");
diff --git a/clap.zig b/clap.zig
index 7ce5322..8339b23 100644
--- a/clap.zig
+++ b/clap.zig
@@ -6,6 +6,7 @@ const heap = std.heap;
6const io = std.io; 6const io = std.io;
7const math = std.math; 7const math = std.math;
8const mem = std.mem; 8const mem = std.mem;
9const meta = std.meta;
9const process = std.process; 10const process = std.process;
10const testing = std.testing; 11const testing = std.testing;
11 12
@@ -25,6 +26,7 @@ pub const Names = struct {
25 /// '--' prefix 26 /// '--' prefix
26 long: ?[]const u8 = null, 27 long: ?[]const u8 = null,
27 28
29 /// The longest of the possible names this `Names` struct can represent.
28 pub fn longest(names: *const Names) Longest { 30 pub fn longest(names: *const Names) Longest {
29 if (names.long) |long| 31 if (names.long) |long|
30 return .{ .kind = .long, .name = long }; 32 return .{ .kind = .long, .name = long };
@@ -73,7 +75,7 @@ pub const Values = enum {
73/// * Positional parameters must take a value. 75/// * Positional parameters must take a value.
74pub fn Param(comptime Id: type) type { 76pub fn Param(comptime Id: type) type {
75 return struct { 77 return struct {
76 id: Id = Id{}, 78 id: Id,
77 names: Names = Names{}, 79 names: Names = Names{},
78 takes_value: Values = .none, 80 takes_value: Values = .none,
79 }; 81 };
@@ -199,7 +201,7 @@ pub fn parseParamEx(str: []const u8, end: *usize) !Param(Help) {
199 // * Someone points out how this is a really bad idea. 201 // * Someone points out how this is a really bad idea.
200 @setEvalBranchQuota(std.math.maxInt(u32)); 202 @setEvalBranchQuota(std.math.maxInt(u32));
201 203
202 var res = Param(Help){}; 204 var res = Param(Help){ .id = .{} };
203 var start: usize = 0; 205 var start: usize = 0;
204 var state: enum { 206 var state: enum {
205 start, 207 start,
@@ -449,11 +451,11 @@ test "parseParams" {
449 \\--bb This should be a new param 451 \\--bb This should be a new param
450 \\ 452 \\
451 , &.{ 453 , &.{
452 .{ .names = .{ .short = 's' } }, 454 .{ .id = .{}, .names = .{ .short = 's' } },
453 .{ .names = .{ .long = "str" } }, 455 .{ .id = .{}, .names = .{ .long = "str" } },
454 .{ .names = .{ .long = "str-str" } }, 456 .{ .id = .{}, .names = .{ .long = "str-str" } },
455 .{ .names = .{ .long = "str_str" } }, 457 .{ .id = .{}, .names = .{ .long = "str_str" } },
456 .{ .names = .{ .short = 's', .long = "str" } }, 458 .{ .id = .{}, .names = .{ .short = 's', .long = "str" } },
457 .{ 459 .{
458 .id = .{ .val = "str" }, 460 .id = .{ .val = "str" },
459 .names = .{ .long = "str" }, 461 .names = .{ .long = "str" },
@@ -671,6 +673,7 @@ pub fn parse(
671 }; 673 };
672} 674}
673 675
676/// The result of `parse`. Is owned by the caller and should be freed with `deinit`.
674pub fn Result( 677pub fn Result(
675 comptime Id: type, 678 comptime Id: type,
676 comptime params: []const Param(Id), 679 comptime params: []const Param(Id),
@@ -721,14 +724,13 @@ pub fn parseEx(
721 opt: ParseOptions, 724 opt: ParseOptions,
722) !ResultEx(Id, params, value_parsers) { 725) !ResultEx(Id, params, value_parsers) {
723 const allocator = opt.allocator; 726 const allocator = opt.allocator;
724 var positionals = std.ArrayList( 727 const Positional = FindPositionalType(Id, params, value_parsers);
725 FindPositionalType(Id, params, value_parsers),
726 ).init(allocator);
727 728
729 var positionals = std.ArrayList(Positional).init(allocator);
728 var arguments = Arguments(Id, params, value_parsers, .list){}; 730 var arguments = Arguments(Id, params, value_parsers, .list){};
729 errdefer deinitArgs(Id, params, value_parsers, .list, allocator, &arguments); 731 errdefer deinitArgs(Id, params, allocator, &arguments);
730 732
731 var stream = streaming.Clap(Id, @typeInfo(@TypeOf(iter)).Pointer.child){ 733 var stream = streaming.Clap(Id, meta.Child(@TypeOf(iter))){
732 .params = params, 734 .params = params,
733 .iter = iter, 735 .iter = iter,
734 .diagnostic = opt.diagnostic, 736 .diagnostic = opt.diagnostic,
@@ -756,8 +758,10 @@ pub fn parseEx(
756 try res; 758 try res;
757 } 759 }
758 760
761 // We are done parsing, but our arguments are stored in lists, and not slices. Map the list
762 // fields to slices and return that.
759 var result_args = Arguments(Id, params, value_parsers, .slice){}; 763 var result_args = Arguments(Id, params, value_parsers, .slice){};
760 inline for (@typeInfo(@TypeOf(arguments)).Struct.fields) |field| { 764 inline for (meta.fields(@TypeOf(arguments))) |field| {
761 if (@typeInfo(field.field_type) == .Struct and 765 if (@typeInfo(field.field_type) == .Struct and
762 @hasDecl(field.field_type, "toOwnedSlice")) 766 @hasDecl(field.field_type, "toOwnedSlice"))
763 { 767 {
@@ -803,6 +807,7 @@ fn parseArg(
803 } 807 }
804} 808}
805 809
810/// The result of `parseEx`. Is owned by the caller and should be freed with `deinit`.
806pub fn ResultEx( 811pub fn ResultEx(
807 comptime Id: type, 812 comptime Id: type,
808 comptime params: []const Param(Id), 813 comptime params: []const Param(Id),
@@ -814,7 +819,7 @@ pub fn ResultEx(
814 allocator: mem.Allocator, 819 allocator: mem.Allocator,
815 820
816 pub fn deinit(result: *@This()) void { 821 pub fn deinit(result: *@This()) void {
817 deinitArgs(Id, params, value_parsers, .slice, result.allocator, &result.args); 822 deinitArgs(Id, params, result.allocator, &result.args);
818 result.allocator.free(result.positionals); 823 result.allocator.free(result.positionals);
819 } 824 }
820 }; 825 };
@@ -825,15 +830,22 @@ fn FindPositionalType(
825 comptime params: []const Param(Id), 830 comptime params: []const Param(Id),
826 comptime value_parsers: anytype, 831 comptime value_parsers: anytype,
827) type { 832) type {
833 const pos = findPositional(Id, params) orelse return []const u8;
834 return ParamType(Id, pos, value_parsers);
835}
836
837fn findPositional(comptime Id: type, params: []const Param(Id)) ?Param(Id) {
828 for (params) |param| { 838 for (params) |param| {
829 const longest = param.names.longest(); 839 const longest = param.names.longest();
830 if (longest.kind == .positinal) 840 if (longest.kind == .positinal)
831 return ParamType(Id, param, value_parsers); 841 return param;
832 } 842 }
833 843
834 return []const u8; 844 return null;
835} 845}
836 846
847/// Given a parameter figure out which type that parameter is parsed into when using the correct
848/// parser from `value_parsers`.
837fn ParamType( 849fn ParamType(
838 comptime Id: type, 850 comptime Id: type,
839 comptime param: Param(Id), 851 comptime param: Param(Id),
@@ -846,13 +858,13 @@ fn ParamType(
846 return parsers.Result(@TypeOf(parser)); 858 return parsers.Result(@TypeOf(parser));
847} 859}
848 860
861/// Deinitializes a struct of type `Argument`. Since the `Argument` type is generated, and we
862/// cannot add the deinit declaration to it, we declare it here instead.
849fn deinitArgs( 863fn deinitArgs(
850 comptime Id: type, 864 comptime Id: type,
851 comptime params: []const Param(Id), 865 comptime params: []const Param(Id),
852 comptime value_parsers: anytype,
853 comptime multi_arg_kind: MultiArgKind,
854 allocator: mem.Allocator, 866 allocator: mem.Allocator,
855 arguments: *Arguments(Id, params, value_parsers, multi_arg_kind), 867 arguments: anytype,
856) void { 868) void {
857 inline for (params) |param| { 869 inline for (params) |param| {
858 const longest = comptime param.names.longest(); 870 const longest = comptime param.names.longest();
@@ -861,15 +873,22 @@ fn deinitArgs(
861 if (param.takes_value != .many) 873 if (param.takes_value != .many)
862 continue; 874 continue;
863 875
864 switch (multi_arg_kind) { 876 const field = @field(arguments, longest.name);
865 .slice => allocator.free(@field(arguments, longest.name)), 877
866 .list => @field(arguments, longest.name).deinit(allocator), 878 // If the multi value field is a struct, we know it is a list and should be deinited.
879 // Otherwise, it is a slice that should be freed.
880 switch (@typeInfo(@TypeOf(field))) {
881 .Struct => @field(arguments, longest.name).deinit(allocator),
882 else => allocator.free(@field(arguments, longest.name)),
867 } 883 }
868 } 884 }
869} 885}
870 886
871const MultiArgKind = enum { slice, list }; 887const MultiArgKind = enum { slice, list };
872 888
889/// Turn a list of parameters into a struct with one field for each none positional parameter.
890/// The type of each parameter field is determined by `ParamType`. Positional arguments will not
891/// havea field in this struct.
873fn Arguments( 892fn Arguments(
874 comptime Id: type, 893 comptime Id: type,
875 comptime params: []const Param(Id), 894 comptime params: []const Param(Id),
@@ -885,27 +904,21 @@ fn Arguments(
885 continue; 904 continue;
886 905
887 const T = ParamType(Id, param, value_parsers); 906 const T = ParamType(Id, param, value_parsers);
888 const FieldType = switch (param.takes_value) { 907 const default_value = switch (param.takes_value) {
889 .none => bool, 908 .none => false,
890 .one => ?T, 909 .one => @as(?T, null),
891 .many => switch (multi_arg_kind) { 910 .many => switch (multi_arg_kind) {
892 .slice => []const T, 911 .slice => @as([]const T, &[_]T{}),
893 .list => std.ArrayListUnmanaged(T), 912 .list => std.ArrayListUnmanaged(T){},
894 }, 913 },
895 }; 914 };
915
896 fields[i] = .{ 916 fields[i] = .{
897 .name = longest.name, 917 .name = longest.name,
898 .field_type = FieldType, 918 .field_type = @TypeOf(default_value),
899 .default_value = switch (param.takes_value) { 919 .default_value = @ptrCast(*const anyopaque, &default_value),
900 .none => &false,
901 .one => &@as(?T, null),
902 .many => switch (multi_arg_kind) {
903 .slice => &@as([]const T, &[_]T{}),
904 .list => &std.ArrayListUnmanaged(T){},
905 },
906 },
907 .is_comptime = false, 920 .is_comptime = false,
908 .alignment = @alignOf(FieldType), 921 .alignment = @alignOf(@TypeOf(default_value)),
909 }; 922 };
910 i += 1; 923 i += 1;
911 } 924 }