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 }