1 /// Contains a type to generate help text for a command. 2 module jaster.cli.commandhelptext; 3 4 import std.array; 5 import jaster.cli.infogen, jaster.cli.result, jaster.cli.helptext, jaster.cli.binder; 6 7 /++ 8 + A helper struct that will generate help text for a given command. 9 + 10 + Description: 11 + This struct will construct a `HelpTextBuilderSimple` (via `toBuilder`, or a string via `toString`) 12 + that is populated via the information provided by the arguments found within `CommandT`, and also the information 13 + attached to `CommandT` itself. 14 + 15 + Here is an example of a fully-featured piece of help text generated by this struct: 16 + 17 + ``` 18 + Usage: mytool MyCommand <InputFile> <OutputFile> <CompressionLevel> [-v|--verbose] [--encoding] 19 + 20 + Description: 21 + This is a command that transforms the InputFile into an OutputFile 22 + 23 + Positional Args: 24 + InputFile - The input file. 25 + OutputFile - The output file. 26 + 27 + Named Args: 28 + -v,--verbose - Verbose output 29 + 30 + Utility: 31 + Utility arguments used to modify the output. 32 + 33 + CompressionLevel - How much to compress the file. 34 + --encoding - Sets the encoding to use. 35 + ``` 36 + 37 + The following UDAs are taken into account when generating the help text: 38 + 39 + * `Command` 40 + 41 + * `CommandNamedArg` 42 + 43 + * `CommandPositionalArg` 44 + 45 + * `CommandArgGroup` 46 + 47 + Furthermore, certain aspects such as whether an argument is nullable or not are reflected within the help text output. 48 + 49 + Params: 50 + CommandT = The command to create the help text for. 51 + ArgBinderInstance = An instance of `ArgBinder`. Currently this is unused, but in the future this may be useful. 52 + ++/ 53 struct CommandHelpText(alias CommandT, alias ArgBinderInstance = ArgBinder!()) 54 { 55 /// The `CommandInfo` for the `CommandT`, `ArgBinderInstance` combo. 56 enum Info = getCommandInfoFor!(CommandT, ArgBinderInstance); 57 58 /++ 59 + Creates a `HelpTextBuilderSimple` which is populated with all the information available from `CommandT`. 60 + 61 + Params: 62 + appName = The name of your application, this is displayed within the help text's "usage" string. 63 + 64 + Returns: 65 + A `HelpTextBuilderSimple` which you can then either further customise, or call `.toString` on. 66 + ++/ 67 HelpTextBuilderSimple toBuilder(string appName) const 68 { 69 auto builder = new HelpTextBuilderSimple(); 70 71 void handleGroup(CommandArgGroup uda) 72 { 73 if(uda.isNull) 74 return; 75 76 builder.setGroupDescription(uda.name, uda.description); 77 } 78 79 foreach(arg; Info.namedArgs) 80 { 81 builder.addNamedArg( 82 (arg.group.isNull) ? null : arg.group.name, 83 arg.uda.pattern.byEach.array, 84 arg.uda.description, 85 cast(ArgIsOptional)((arg.existence & CommandArgExistence.optional) > 0) 86 ); 87 handleGroup(arg.group); 88 } 89 90 foreach(arg; Info.positionalArgs) 91 { 92 builder.addPositionalArg( 93 (arg.group.isNull) ? null : arg.group.name, 94 arg.uda.position, 95 arg.uda.description, 96 cast(ArgIsOptional)((arg.existence & CommandArgExistence.optional) > 0), 97 arg.uda.name 98 ); 99 handleGroup(arg.group); 100 } 101 102 builder.commandName = appName ~ " " ~ Info.pattern.defaultPattern; 103 builder.description = Info.description; 104 105 return builder; 106 } 107 108 /// Returns: The result of `toBuilder(appName).toString()`. 109 string toString(string appName) const 110 { 111 return this.toBuilder(appName).toString(); 112 } 113 } 114 115 // To get around a limiation of not being able to use Nullable in ArgumentInfo 116 private bool isNull(CommandArgGroup group) 117 { 118 return group == CommandArgGroup.init; 119 }