summaryrefslogtreecommitdiff
path: root/clap.zig
diff options
context:
space:
mode:
Diffstat (limited to 'clap.zig')
-rw-r--r--clap.zig129
1 files changed, 94 insertions, 35 deletions
diff --git a/clap.zig b/clap.zig
index 21590a2..8d6594c 100644
--- a/clap.zig
+++ b/clap.zig
@@ -675,7 +675,7 @@ pub fn Result(
675) type { 675) type {
676 return struct { 676 return struct {
677 args: Arguments(Id, params, value_parsers, .slice), 677 args: Arguments(Id, params, value_parsers, .slice),
678 positionals: []const FindPositionalType(Id, params, value_parsers), 678 positionals: Positionals(Id, params, value_parsers, .slice),
679 exe_arg: ?[]const u8, 679 exe_arg: ?[]const u8,
680 arena: std.heap.ArenaAllocator, 680 arena: std.heap.ArenaAllocator,
681 681
@@ -718,11 +718,13 @@ pub fn parseEx(
718 opt: ParseOptions, 718 opt: ParseOptions,
719) !ResultEx(Id, params, value_parsers) { 719) !ResultEx(Id, params, value_parsers) {
720 const allocator = opt.allocator; 720 const allocator = opt.allocator;
721 const Positional = FindPositionalType(Id, params, value_parsers);
722 721
723 var positionals = std.ArrayList(Positional).init(allocator); 722 var positional_count: usize = 0;
723 var positionals = initPositionals(Positionals(Id, params, value_parsers, .list));
724 errdefer deinitPositionals(&positionals, allocator);
725
724 var arguments = Arguments(Id, params, value_parsers, .list){}; 726 var arguments = Arguments(Id, params, value_parsers, .list){};
725 errdefer deinitArgs(Id, params, allocator, &arguments); 727 errdefer deinitArgs(&arguments, allocator);
726 728
727 var stream = streaming.Clap(Id, std.meta.Child(@TypeOf(iter))){ 729 var stream = streaming.Clap(Id, std.meta.Child(@TypeOf(iter))){
728 .params = params, 730 .params = params,
@@ -753,9 +755,14 @@ pub fn parseEx(
753 }, 755 },
754 }, 756 },
755 .positional => { 757 .positional => {
756 try positionals.append(try parser(arg.value.?)); 758 switch (@typeInfo(@TypeOf(positionals))) {
757 if (opt.terminating_positional <= positionals.items.len - 1) 759 .optional => positionals = try parser(arg.value.?),
760 else => try positionals.append(allocator, try parser(arg.value.?)),
761 }
762 if (opt.terminating_positional <= positional_count)
758 break :arg_loop; 763 break :arg_loop;
764
765 positional_count += 1;
759 }, 766 },
760 } 767 }
761 } 768 }
@@ -775,9 +782,15 @@ pub fn parseEx(
775 } 782 }
776 } 783 }
777 784
785 // We are done parsing, but our positionals are stored in lists, and not slices.
786 const result_positionals = switch (@typeInfo(@TypeOf(positionals))) {
787 .optional => positionals,
788 else => try positionals.toOwnedSlice(allocator),
789 };
790
778 return ResultEx(Id, params, value_parsers){ 791 return ResultEx(Id, params, value_parsers){
779 .args = result_args, 792 .args = result_args,
780 .positionals = try positionals.toOwnedSlice(), 793 .positionals = result_positionals,
781 .allocator = allocator, 794 .allocator = allocator,
782 }; 795 };
783} 796}
@@ -790,23 +803,46 @@ pub fn ResultEx(
790) type { 803) type {
791 return struct { 804 return struct {
792 args: Arguments(Id, params, value_parsers, .slice), 805 args: Arguments(Id, params, value_parsers, .slice),
793 positionals: []const FindPositionalType(Id, params, value_parsers), 806 positionals: Positionals(Id, params, value_parsers, .slice),
794 allocator: std.mem.Allocator, 807 allocator: std.mem.Allocator,
795 808
796 pub fn deinit(result: *@This()) void { 809 pub fn deinit(result: *@This()) void {
797 deinitArgs(Id, params, result.allocator, &result.args); 810 deinitArgs(&result.args, result.allocator);
798 result.allocator.free(result.positionals); 811 deinitPositionals(&result.positionals, result.allocator);
799 } 812 }
800 }; 813 };
801} 814}
802 815
803fn FindPositionalType( 816fn Positionals(
804 comptime Id: type, 817 comptime Id: type,
805 comptime params: []const Param(Id), 818 comptime params: []const Param(Id),
806 comptime value_parsers: anytype, 819 comptime value_parsers: anytype,
820 comptime multi_arg_kind: MultiArgKind,
807) type { 821) type {
808 const pos = findPositional(Id, params) orelse return []const u8; 822 const pos = findPositional(Id, params) orelse return ?void;
809 return ParamType(Id, pos, value_parsers); 823 const T = ParamType(Id, pos, value_parsers);
824 if (pos.takes_value == .many)
825 return switch (multi_arg_kind) {
826 .slice => []const T,
827 .list => std.ArrayListUnmanaged(T),
828 };
829
830 return ?T;
831}
832
833fn initPositionals(comptime T: type) T {
834 return switch (@typeInfo(T)) {
835 .optional => null,
836 else => .{},
837 };
838}
839
840fn deinitPositionals(positionals: anytype, allocator: std.mem.Allocator) void {
841 switch (@typeInfo(@TypeOf(positionals.*))) {
842 .optional => {},
843 .@"struct" => positionals.deinit(allocator),
844 else => allocator.free(positionals.*),
845 }
810} 846}
811 847
812fn findPositional(comptime Id: type, params: []const Param(Id)) ?Param(Id) { 848fn findPositional(comptime Id: type, params: []const Param(Id)) ?Param(Id) {
@@ -835,26 +871,12 @@ fn ParamType(
835 871
836/// Deinitializes a struct of type `Argument`. Since the `Argument` type is generated, and we 872/// Deinitializes a struct of type `Argument`. Since the `Argument` type is generated, and we
837/// cannot add the deinit declaration to it, we declare it here instead. 873/// cannot add the deinit declaration to it, we declare it here instead.
838fn deinitArgs( 874fn deinitArgs(arguments: anytype, allocator: std.mem.Allocator) void {
839 comptime Id: type, 875 inline for (@typeInfo(@TypeOf(arguments.*)).@"struct".fields) |field| {
840 comptime params: []const Param(Id), 876 switch (@typeInfo(field.type)) {
841 allocator: std.mem.Allocator, 877 .int, .optional => {},
842 arguments: anytype, 878 .@"struct" => @field(arguments, field.name).deinit(allocator),
843) void { 879 else => allocator.free(@field(arguments, field.name)),
844 inline for (params) |param| {
845 const longest = comptime param.names.longest();
846 if (longest.kind == .positional)
847 continue;
848 if (param.takes_value != .many)
849 continue;
850
851 const field = @field(arguments, longest.name);
852
853 // If the multi value field is a struct, we know it is a list and should be deinited.
854 // Otherwise, it is a slice that should be freed.
855 switch (@typeInfo(@TypeOf(field))) {
856 .@"struct" => @field(arguments, longest.name).deinit(allocator),
857 else => allocator.free(@field(arguments, longest.name)),
858 } 880 }
859 } 881 }
860} 882}
@@ -948,6 +970,43 @@ test "different assignment separators" {
948 try std.testing.expectEqualSlices(usize, &.{ 0, 1, 2, 3 }, res.args.aa); 970 try std.testing.expectEqualSlices(usize, &.{ 0, 1, 2, 3 }, res.args.aa);
949} 971}
950 972
973test "single positional" {
974 const params = comptime parseParamsComptime(
975 \\<str>
976 \\
977 );
978
979 {
980 var iter = args.SliceIterator{ .args = &.{} };
981 var res = try parseEx(Help, &params, parsers.default, &iter, .{
982 .allocator = std.testing.allocator,
983 });
984 defer res.deinit();
985
986 try std.testing.expect(res.positionals == null);
987 }
988
989 {
990 var iter = args.SliceIterator{ .args = &.{"a"} };
991 var res = try parseEx(Help, &params, parsers.default, &iter, .{
992 .allocator = std.testing.allocator,
993 });
994 defer res.deinit();
995
996 try std.testing.expectEqualStrings("a", res.positionals.?);
997 }
998
999 {
1000 var iter = args.SliceIterator{ .args = &.{ "a", "b" } };
1001 var res = try parseEx(Help, &params, parsers.default, &iter, .{
1002 .allocator = std.testing.allocator,
1003 });
1004 defer res.deinit();
1005
1006 try std.testing.expectEqualStrings("b", res.positionals.?);
1007 }
1008}
1009
951test "everything" { 1010test "everything" {
952 const params = comptime parseParamsComptime( 1011 const params = comptime parseParamsComptime(
953 \\-a, --aa 1012 \\-a, --aa
@@ -955,7 +1014,7 @@ test "everything" {
955 \\-c, --cc <str> 1014 \\-c, --cc <str>
956 \\-d, --dd <usize>... 1015 \\-d, --dd <usize>...
957 \\-h 1016 \\-h
958 \\<str> 1017 \\<str>...
959 \\ 1018 \\
960 ); 1019 );
961 1020
@@ -984,7 +1043,7 @@ test "terminating positional" {
984 \\-c, --cc <str> 1043 \\-c, --cc <str>
985 \\-d, --dd <usize>... 1044 \\-d, --dd <usize>...
986 \\-h 1045 \\-h
987 \\<str> 1046 \\<str>...
988 \\ 1047 \\
989 ); 1048 );
990 1049