| ... | ... | @@ -40,15 +40,37 @@ pub const Error = error{ |
| 40 | 40 | /// `Args` is a struct that you define looking like this: |
| 41 | 41 | /// ``` |
| 42 | 42 | /// const Args = struct { |
| 43 | /// pub const description = "this program does a thing"; |
| 43 | 44 | /// named: struct { |
| 44 | | /// // ... |
| 45 | /// verbose: bool = false, |
| 46 | /// output: [:0]const u8, |
| 47 | /// pub const output_help = "path to output file"; |
| 45 | 48 | /// }, |
| 46 | 49 | /// positional: struct { |
| 47 | | /// // ... |
| 50 | /// input: []const u8, |
| 51 | /// args: []const []const u8 = &.{}, |
| 48 | 52 | /// }, |
| 49 | 53 | /// }; |
| 50 | 54 | /// ``` |
| 51 | | /// Either or both of `named` and `positional` may be omitted, which is effectively equivalent to them having no fields. |
| 55 | /// Which results in this generated `--help` output: |
| 56 | /// ``` |
| 57 | /// usage: <prog> [options] --output=string input [args...] |
| 58 | /// |
| 59 | /// this program does a thing |
| 60 | /// |
| 61 | /// positional arguments: |
| 62 | /// input string. required |
| 63 | /// args string. can be specified multiple times |
| 64 | /// |
| 65 | /// named arguments: |
| 66 | /// --verbose default: --no-verbose |
| 67 | /// --output=string required. path to output file |
| 68 | /// --help print this help and exit |
| 69 | /// ``` |
| 70 | /// Either or both of `named` and `positional` may be omitted, which is effectively equivalent to declaring them as `struct {}`. |
| 71 | /// If `description` is declared, it is concatenated into the help output. |
| 72 | /// If any `pub const <name>_help` accompanies a field `<name>` in either `named` or `positional`, |
| 73 | /// it is included in that argument's help text. |
| 52 | 74 | /// |
| 53 | 75 | /// The sequence of arg strings from the `ArgIterator` is parsed to determine named and positional arguments. |
| 54 | 76 | /// |
| ... | ... | @@ -160,7 +182,7 @@ test parse { |
| 160 | 182 | /// First positional (non-named) argument: |
| 161 | 183 | input: [:0]const u8 = "", |
| 162 | 184 | /// Second positional argument is declared as optional: |
| 163 | | repititions: u32 = 1, |
| 185 | repetitions: u32 = 1, |
| 164 | 186 | /// Receives the rest of the positional arguments. |
| 165 | 187 | @"the-rest": []const [:0]const u8 = &.{}, |
| 166 | 188 | }, |
| ... | ... | @@ -717,20 +739,20 @@ fn printGeneratedHelp(comptime Args: type, writer: ?*Writer, prog: []const u8) v |
| 717 | 739 | arguments_table = arguments_table ++ .{&[_][]const u8{ |
| 718 | 740 | " " ++ field.name, |
| 719 | 741 | @typeName(field.type) ++ " " ++ |
| 720 | | if (field.defaultValue()) |default| |
| 742 | (if (field.defaultValue()) |default| |
| 721 | 743 | "default: " ++ std.fmt.comptimePrint("{}", .{default}) |
| 722 | 744 | else |
| 723 | | "required", |
| 745 | "required") ++ argHelp(Args, "positional", field.name), |
| 724 | 746 | }}; |
| 725 | 747 | }, |
| 726 | 748 | .@"enum" => { |
| 727 | 749 | arguments_table = arguments_table ++ .{&[_][]const u8{ |
| 728 | 750 | " " ++ field.name, |
| 729 | 751 | comptime enumValuesExpr(field.type) ++ ". " ++ |
| 730 | | if (field.defaultValue()) |default| |
| 752 | (if (field.defaultValue()) |default| |
| 731 | 753 | "default: " ++ @tagName(default) |
| 732 | 754 | else |
| 733 | | "required", |
| 755 | "required") ++ argHelp(Args, "positional", field.name), |
| 734 | 756 | }}; |
| 735 | 757 | }, |
| 736 | 758 | .pointer => |ptrInfo| { |
| ... | ... | @@ -739,10 +761,10 @@ fn printGeneratedHelp(comptime Args: type, writer: ?*Writer, prog: []const u8) v |
| 739 | 761 | arguments_table = arguments_table ++ .{&[_][]const u8{ |
| 740 | 762 | " " ++ field.name, |
| 741 | 763 | "string. " ++ |
| 742 | | if (field.defaultValue()) |default| |
| 764 | (if (field.defaultValue()) |default| |
| 743 | 765 | "default: " ++ quoteIfEmpty(default) |
| 744 | 766 | else |
| 745 | | "required", |
| 767 | "required") ++ argHelp(Args, "positional", field.name), |
| 746 | 768 | }}; |
| 747 | 769 | } else { |
| 748 | 770 | // Array |
| ... | ... | @@ -755,7 +777,7 @@ fn printGeneratedHelp(comptime Args: type, writer: ?*Writer, prog: []const u8) v |
| 755 | 777 | }; |
| 756 | 778 | arguments_table = arguments_table ++ .{&[_][]const u8{ |
| 757 | 779 | " " ++ field.name, |
| 758 | | type_name ++ ". can be specified multiple times", |
| 780 | type_name ++ ". can be specified multiple times" ++ argHelp(Args, "positional", field.name), |
| 759 | 781 | }}; |
| 760 | 782 | } |
| 761 | 783 | }, |
| ... | ... | @@ -769,31 +791,31 @@ fn printGeneratedHelp(comptime Args: type, writer: ?*Writer, prog: []const u8) v |
| 769 | 791 | .bool => { |
| 770 | 792 | if (field.defaultValue()) |default| { |
| 771 | 793 | if (default) { |
| 772 | | arguments_table = arguments_table ++ .{&[_][]const u8{ " --no-" ++ field.name, "default: --" ++ field.name }}; |
| 794 | arguments_table = arguments_table ++ .{&[_][]const u8{ " --no-" ++ field.name, "default: --" ++ field.name ++ argHelp(Args, "named", field.name) }}; |
| 773 | 795 | } else { |
| 774 | | arguments_table = arguments_table ++ .{&[_][]const u8{ " --" ++ field.name, "default: --no-" ++ field.name }}; |
| 796 | arguments_table = arguments_table ++ .{&[_][]const u8{ " --" ++ field.name, "default: --no-" ++ field.name ++ argHelp(Args, "named", field.name) }}; |
| 775 | 797 | } |
| 776 | 798 | } else { |
| 777 | | arguments_table = arguments_table ++ .{&[_][]const u8{ " --[no-]" ++ field.name, "required" }}; |
| 799 | arguments_table = arguments_table ++ .{&[_][]const u8{ " --[no-]" ++ field.name, "required" ++ argHelp(Args, "named", field.name) }}; |
| 778 | 800 | } |
| 779 | 801 | }, |
| 780 | 802 | .int, .float => { |
| 781 | 803 | arguments_table = arguments_table ++ .{&[_][]const u8{ |
| 782 | 804 | " --" ++ field.name ++ "=" ++ @typeName(field.type), |
| 783 | | if (field.defaultValue()) |default| |
| 805 | (if (field.defaultValue()) |default| |
| 784 | 806 | "default: " ++ std.fmt.comptimePrint("{}", .{default}) |
| 785 | 807 | else |
| 786 | | "required", |
| 808 | "required") ++ argHelp(Args, "named", field.name), |
| 787 | 809 | }}; |
| 788 | 810 | }, |
| 789 | 811 | .@"enum" => { |
| 790 | 812 | arguments_table = arguments_table ++ .{&[_][]const u8{ |
| 791 | 813 | " --" ++ field.name ++ "=enum", |
| 792 | | comptime enumValuesExpr(field.type) ++ " " ++ |
| 793 | | if (field.defaultValue()) |default| |
| 814 | comptime enumValuesExpr(field.type) ++ ". " ++ |
| 815 | (if (field.defaultValue()) |default| |
| 794 | 816 | "default: " ++ @tagName(default) |
| 795 | 817 | else |
| 796 | | "required", |
| 818 | "required") ++ argHelp(Args, "named", field.name), |
| 797 | 819 | }}; |
| 798 | 820 | }, |
| 799 | 821 | .pointer => |ptrInfo| { |
| ... | ... | @@ -801,10 +823,10 @@ fn printGeneratedHelp(comptime Args: type, writer: ?*Writer, prog: []const u8) v |
| 801 | 823 | // String |
| 802 | 824 | arguments_table = arguments_table ++ .{&[_][]const u8{ |
| 803 | 825 | " --" ++ field.name ++ "=string", |
| 804 | | if (field.defaultValue()) |default| |
| 826 | (if (field.defaultValue()) |default| |
| 805 | 827 | "default: " ++ quoteIfEmpty(default) |
| 806 | 828 | else |
| 807 | | "required", |
| 829 | "required") ++ argHelp(Args, "named", field.name), |
| 808 | 830 | }}; |
| 809 | 831 | } else { |
| 810 | 832 | // Array |
| ... | ... | @@ -817,7 +839,7 @@ fn printGeneratedHelp(comptime Args: type, writer: ?*Writer, prog: []const u8) v |
| 817 | 839 | }; |
| 818 | 840 | arguments_table = arguments_table ++ .{&[_][]const u8{ |
| 819 | 841 | " --" ++ field.name ++ "=" ++ type_name, |
| 820 | | "can be specified multiple times", |
| 842 | "can be specified multiple times" ++ argHelp(Args, "named", field.name), |
| 821 | 843 | }}; |
| 822 | 844 | } |
| 823 | 845 | }, |
| ... | ... | @@ -833,6 +855,9 @@ fn printGeneratedHelp(comptime Args: type, writer: ?*Writer, prog: []const u8) v |
| 833 | 855 | } |
| 834 | 856 | |
| 835 | 857 | comptime var help_str: []const u8 = ""; |
| 858 | if (@hasDecl(Args, "description")) { |
| 859 | help_str = "\n\n" ++ Args.description; |
| 860 | } |
| 836 | 861 | inline for (arguments_table) |row| { |
| 837 | 862 | help_str = help_str ++ "\n"; |
| 838 | 863 | inline for (row, 0..) |cell, c| { |
| ... | ... | @@ -856,6 +881,13 @@ fn printGeneratedHelp(comptime Args: type, writer: ?*Writer, prog: []const u8) v |
| 856 | 881 | } |
| 857 | 882 | } |
| 858 | 883 | |
| 884 | inline fn argHelp(comptime Args: type, comptime named_or_positional: []const u8, comptime field_name: []const u8) []const u8 { |
| 885 | const N = @FieldType(Args, named_or_positional); |
| 886 | comptime assert(@hasField(N, field_name)); |
| 887 | if (!@hasDecl(N, field_name ++ "_help")) return ""; |
| 888 | return ". " ++ @field(N, field_name ++ "_help"); |
| 889 | } |
| 890 | |
| 859 | 891 | inline fn quoteIfEmpty(comptime s: []const u8) []const u8 { |
| 860 | 892 | if (s.len == 0) return "''"; |
| 861 | 893 | return s; |