1 module new_examples.top_down.main; 2 3 int main(string[] args) 4 { 5 // The top-down approach means all commands must list their subcommands. 6 // In my opinion this one is easier to understand than the bottom-up approach, 7 // because it is more explicit. 8 // @ParentCommand attributes are ignored if you're taking this approach. 9 10 /* 11 Examples: 12 13 top_down --help 14 15 top_down print --help 16 17 top_down print "Hello world!" 18 19 top_down -silent print "Hello" 20 21 top_down multiply --help 22 23 top_down multiply 1 2 24 25 top_down multiply 5 10 -reciprocal 26 27 top_down multiply 5 0 -reciprocal 28 29 top_down -s multiply 5 0 -reciprocal 30 */ 31 import jcli : matchAndExecuteFromRootCommands; 32 import jcli.argbinder : bindArgumentSimple; 33 return matchAndExecuteFromRootCommands!(bindArgumentSimple, RootCommand)(args[1 .. $]); 34 } 35 36 import jcli.core.udas; 37 import jcli.core.flags : ArgConfig; 38 import std.stdio : writeln; 39 40 @CommandDefault("The root command, which gives some common context to the subcommands.") 41 @(Subcommands!(PrintCommand, MultiplyCommand)) 42 struct RootCommand 43 { 44 @ArgNamed("silent|s", "Silences all console logs (opt-in).") 45 @(ArgConfig.parseAsFlag) 46 bool silent; 47 48 void onIntermediateExecute() 49 { 50 maybeWriteln("Intermediate execute..."); 51 } 52 53 // Just forward all args to `writeln`. 54 void maybeWriteln(Args...)(auto ref Args args) 55 { 56 if (!silent) 57 writeln(args); 58 } 59 } 60 61 // This subcommand is terminal = does not define any subcommands of its own. 62 // You can define subcommands to any level of depth. 63 // @(Subcommands!(SomeCommandA, SomeCommandB)) 64 @Command("print", "Prints whatever it's given") 65 struct PrintCommand 66 { 67 // @ParentCommand 68 RootCommand* root; 69 70 @ArgPositional("Some string that will be printed.") 71 string whatever; 72 73 void onExecute() 74 { 75 // I'm using the helper here, but you can still access `root.silent` 76 root.maybeWriteln(whatever); 77 } 78 } 79 80 @Command("multiply", "Multiplies the 2 given numbers") 81 struct MultiplyCommand 82 { 83 RootCommand* root; 84 85 @("Whether to return the reciprocal of the answer.") 86 @(ArgConfig.parseAsFlag) 87 bool reciprocal; 88 89 // TODO: 90 // Positional aggregate arguments are not supported currently. 91 // Could mutiply up N numbers if we had that feature. 92 93 // ArgGroup is a convenient way to add info to both of these arguments. 94 @ArgGroup("numbers", "Numbers to multiply together") 95 { 96 @ArgPositional 97 float a; 98 99 @ArgPositional 100 float b; 101 } 102 103 int onExecute() 104 { 105 float result = a * b; 106 if (reciprocal) 107 { 108 import std.math; 109 if (isClose(result, 0f)) 110 { 111 root.maybeWriteln("Error: division by 0."); 112 return 1; 113 } 114 result = 1 / result; 115 } 116 root.maybeWriteln(result); 117 return 0; 118 } 119 }