1 /**
2  * Parsed arguments module.
3  *
4  * This module contains functionality for handling the parsed command line arguments.
5  *
6  * `ProgramArgs` instance is created and returned by `parse` function. It contains values of flags,
7  * options and arguments, which can be read with `.flag`, `.option` and `.arg` functions respectively.
8  * Those functions work on _unique names_, not on option full/short names such as `-l` or `--help`.
9  *
10  * For repeating options and arguments, plural form functions can be used: `.options`, `.args`,
11  * which return all values rather than last one.
12  *
13  * When a command or program has sub-commands returned `ProgramArgs` object forms a hierarchy,
14  * where every subcommand is another instance of `ProgramArgs`, starting from root which is program args, going down
15  * to selected sub-command.
16  *
17  * To simplify working with subcommands, you can use `on` command that allows to register command handlers
18  * with a simple interface. E.g. consider git-like tool:
19  *
20  * ---
21  * auto program = new Program("grit")
22  *         .add(new Flag("v", "verbose", "verbosity"))
23  *         .add(new Command("branch", "branch management")
24  *             .add(new Command("add", "adds branch")
25  *                 .add(new Argument("name"))
26  *             )
27  *             .add(new Command("rm", "removes branch")
28  *                 .add(new Argument("name"))
29  *             )
30  *         )
31  *      ;
32  * auto programArgs = program.parse(args);
33  * programArgs
34  *   .on("branch", (args) {
35  *       writeln("verbosity: ", args.flag("verbose"));
36  *       args.on("rm", (args) { writeln("removing branch ", args.arg("name")); })
37  *           .on("add", (args) { writeln("adding branch", args.arg("name")); })
38  *   })
39  * ---
40  *
41  * See_Also:
42  *  ProgramArgs
43  */
44 module commandr.args;
45 
46 import commandr.program;
47 
48 
49 /**
50  * Parsed program/command arguments.
51  *
52  * Note: All functions here work on flag/option/argument names, not short or long names.
53  * option -> options multi
54  * names
55  * commands
56  */
57 public class ProgramArgs {
58     /// Program or command name
59     public string name;
60 
61     package {
62         int[string] _flags;
63         string[][string] _options;
64         string[][string] _args;
65         ProgramArgs _parent;
66         ProgramArgs _command;
67     }
68 
69     package ProgramArgs copy() {
70         ProgramArgs a = new ProgramArgs();
71         a._flags = _flags.dup;
72         a._options = _options.dup;
73         a._args = _args.dup;
74         return a;
75     }
76 
77     /**
78      * Checks for flag value.
79      *
80      * Params:
81      *   name - flag name to check
82      *
83      * Returns:
84      *  true if flag has been passed at least once, false otherwise.
85      *
86      * See_Also:
87      *  occurencesOf
88      */
89     public bool hasFlag(string name) {
90         return ((name in _flags) != null && _flags[name] > 0);
91     }
92 
93     /// ditto
94     public alias flag = hasFlag;
95 
96     /**
97      * Gets number of flag occurences.
98      *
99      * For non-repeating flags, returns either 0 or 1.
100      *
101      * Params:
102      *  name - flag name to check
103      *
104      * Returns:
105      *  Number of flag occurences, 0 on none.
106      *
107      * See_Also:
108      *  hasFlag, flag
109      */
110     public int occurencesOf(string name) {
111         if (!hasFlag(name)) {
112             return 0;
113         }
114         return _flags[name];
115     }
116 
117     /**
118      * Gets option value.
119      *
120      * In case of repeating option, returns last value.
121      *
122      * Params:
123      *   name - name of option to get
124      *   defaultValue - default value if option is not set
125      *
126      * Returns:
127      *   Last option specified, or defaultValue if none
128      *
129      * See_Also:
130      *  options, optionAll
131      */
132     public string option(string name, string defaultValue = null) {
133         string[]* entryPtr = name in _options;
134         if (!entryPtr) {
135             return defaultValue;
136         }
137 
138         if ((*entryPtr).length == 0) {
139             return defaultValue;
140         }
141 
142         return (*entryPtr)[$-1];
143     }
144 
145     /**
146      * Gets all option values.
147      *
148      * In case of non-repeating option, returns array with one value.
149      *
150      * Params:
151      *   name - name of option to get
152      *   defaultValue - default value if option is not set
153      *
154      * Returns:
155      *   Option values, or defaultValue if none
156      *
157      * See_Also:
158      *  option
159      */
160     public string[] optionAll(string name, string[] defaultValue = null) {
161         string[]* entryPtr = name in _options;
162         if (!entryPtr) {
163             return defaultValue;
164         }
165         return *entryPtr;
166     }
167 
168     /// ditto
169     alias options = optionAll;
170 
171     /**
172      * Gets argument value.
173      *
174      * In case of repeating arguments, returns last value.
175      *
176      * Params:
177      *   name - name of argument to get
178      *   defaultValue - default value if argument is missing
179      *
180      * Returns:
181      *   Argument values, or defaultValue if none
182      *
183      * See_Also:
184      *  args, argAll
185      */
186     public string arg(string name, string defaultValue = null) {
187         string[]* entryPtr = name in _args;
188         if (!entryPtr) {
189             return defaultValue;
190         }
191 
192         if ((*entryPtr).length == 0) {
193             return defaultValue;
194         }
195 
196         return (*entryPtr)[$-1];
197     }
198 
199     /**
200      * Gets all argument values.
201      *
202      * In case of non-repeating arguments, returns array with one value.
203      *
204      * Params:
205      *   name - name of argument to get
206      *   defaultValue - default value if argument is missing
207      *
208      * Returns:
209      *   Argument values, or defaultValue if none
210      *
211      * See_Also:
212      *  arg
213      */
214     public string[] argAll(string name, string[] defaultValue = null) {
215         string[]* entryPtr = name in _args;
216         if (!entryPtr) {
217             return defaultValue;
218         }
219         return *entryPtr;
220     }
221 
222     /// ditto
223     alias args = argAll;
224 
225 
226     /**
227      * Gets subcommand arguments.
228      *
229      * See_Also:
230      *   on, parent
231      */
232     public ProgramArgs command() {
233         return _command;
234     }
235 
236     /**
237      * Gets parent `ProgramArgs`, if any.
238      *
239      * See_Also:
240      *   command
241      */
242     public ProgramArgs parent() {
243         return _parent;
244     }
245 
246     /**
247      * Calls `handler` if user specified `command` subcommand.
248      *
249      * Example:
250      * ---
251      * auto a = new Program()
252      *      .add(new Command("test"))
253      *      .parse(args);
254      *
255      * a.on("test", (a) {
256      *     writeln("Test!");
257      * });
258      * ---
259      */
260     public typeof(this) on(string command, scope void delegate(ProgramArgs args) handler) {
261         if (_command !is null && _command.name == command) {
262             handler(_command);
263         }
264 
265         return this;
266     }
267 }