diff options
Diffstat (limited to 'clap.zig')
| -rw-r--r-- | clap.zig | 129 |
1 files changed, 94 insertions, 35 deletions
| @@ -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 | ||
| 803 | fn FindPositionalType( | 816 | fn 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 | |||
| 833 | fn initPositionals(comptime T: type) T { | ||
| 834 | return switch (@typeInfo(T)) { | ||
| 835 | .optional => null, | ||
| 836 | else => .{}, | ||
| 837 | }; | ||
| 838 | } | ||
| 839 | |||
| 840 | fn 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 | ||
| 812 | fn findPositional(comptime Id: type, params: []const Param(Id)) ?Param(Id) { | 848 | fn 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. |
| 838 | fn deinitArgs( | 874 | fn 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 | ||
| 973 | test "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, ¶ms, 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, ¶ms, 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, ¶ms, 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 | |||
| 951 | test "everything" { | 1010 | test "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 | ||