1 /*******************************************************************************
2 
3     Application class that provides the standard features needed by command
4     line tools:
5         * Command line parsing
6         * Version support
7 
8     Usage example:
9         See CliApp class' documented unittest
10 
11     Copyright:
12         Copyright (c) 2009-2016 dunnhumby Germany GmbH.
13         All rights reserved.
14 
15     License:
16         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
17         Alternatively, this file may be distributed under the terms of the Tango
18         3-Clause BSD License (see LICENSE_BSD.txt for details).
19 
20 *******************************************************************************/
21 
22 module ocean.util.app.CliApp;
23 
24 
25 import ocean.util.app.Application : Application;
26 import ocean.util.app.ext.model.IArgumentsExtExtension;
27 import ocean.task.IScheduler;
28 
29 import ocean.meta.types.Qualifiers;
30 
31 /*******************************************************************************
32 
33     CliApp class
34 
35 *******************************************************************************/
36 
37 abstract class CliApp : Application, IArgumentsExtExtension
38 {
39     static import ocean.text.Arguments;
40     public alias ocean.text.Arguments.Arguments Arguments;
41 
42     import ocean.util.app.ext.ArgumentsExt;
43     import ocean.util.app.ext.VersionArgsExt;
44     import ocean.util.app.ext.TaskExt;
45 
46     protected import ocean.util.app.ext.VersionInfo : VersionInfo;
47 
48     /***************************************************************************
49 
50         Command line arguments used by the application.
51 
52     ***************************************************************************/
53 
54     public Arguments args;
55 
56     /***************************************************************************
57 
58         Command line arguments extension used by the application.
59 
60     ***************************************************************************/
61 
62     public ArgumentsExt args_ext;
63 
64     /***************************************************************************
65 
66         Version information.
67 
68     ***************************************************************************/
69 
70     public VersionInfo ver;
71 
72     /***************************************************************************
73 
74         Version information extension.
75 
76     ***************************************************************************/
77 
78     public VersionArgsExt ver_ext;
79 
80     /***************************************************************************
81 
82         Extension to start `run` method inside a task.
83 
84     ***************************************************************************/
85 
86     public TaskExt task_ext;
87 
88     /***************************************************************************
89 
90         Struct containing optional constructor arguments. There are enough of
91         these that handling them as default arguments to the ctor is cumbersome.
92 
93     ***************************************************************************/
94 
95     public static struct OptionalSettings
96     {
97         /***********************************************************************
98 
99             How the program is supposed to be invoked.
100 
101         ***********************************************************************/
102 
103         istring usage = null;
104 
105         /***********************************************************************
106 
107             Long description of what the program does and how to use it.
108 
109         ***********************************************************************/
110 
111         istring help = null;
112 
113         /***********************************************************************
114 
115             By default TaskExt is disabled to prevent breaking change for
116             applications already configuring scheduler on their own.
117 
118         ***********************************************************************/
119 
120         bool use_task_ext;
121 
122         /***********************************************************************
123 
124             Only used if `use_task_ext` is set to `true`. Defines scheduler
125             configuration to be used by TaskExt.
126 
127         ***********************************************************************/
128 
129         IScheduler.Configuration scheduler_config;
130     }
131 
132     /***************************************************************************
133 
134         This constructor only sets up the internal state of the class, but does
135         not call any extension or user code.
136 
137         Params:
138             name = Name of the application (to show in the help message)
139             desc = Short description of what the program does (should be
140                          one line only, preferably less than 80 characters)
141             ver = application's version information
142             settings = optional settings (see OptionalSettings, above)
143 
144     ***************************************************************************/
145 
146     public this ( istring name, istring desc, VersionInfo ver,
147         OptionalSettings settings = OptionalSettings.init )
148     {
149         super(name, desc);
150 
151         // Create and register arguments extension
152         this.args_ext = new ArgumentsExt(name, desc, settings.usage,
153             settings.help);
154         this.args = this.args_ext.args;
155         this.args_ext.registerExtension(this);
156         this.registerExtension(this.args_ext);
157 
158         // Create and register version extension
159         this.ver_ext = new VersionArgsExt(ver);
160         this.ver = this.ver_ext.ver;
161         this.args_ext.registerExtension(this.ver_ext);
162         this.registerExtension(this.ver_ext);
163 
164         if (settings.use_task_ext)
165         {
166             this.task_ext = new TaskExt(settings.scheduler_config);
167             // initialises scheduler even if config is not present:
168             this.task_ext.processConfig(null, null);
169         }
170     }
171 
172     /***************************************************************************
173 
174         Run implementation that forwards to the abstract
175         run(Arguments, ConfigParser).
176 
177         Params:
178             args = raw command line arguments
179 
180         Returns:
181             status code to return to the OS
182 
183     ***************************************************************************/
184 
185     override protected int run ( istring[] args )
186     {
187         if (this.task_ext is null)
188             return this.run(this.args);
189 
190         return this.task_ext.run(&this.mainForTaskExt);
191     }
192 
193     /***************************************************************************
194 
195         Used inside `run` if TaskExt is enabled to workaround double `this`
196         issue with inline delegate literal
197 
198     ***************************************************************************/
199 
200     private int mainForTaskExt ( )
201     {
202         return this.run(this.args);
203     }
204 
205     /***************************************************************************
206 
207         This method must be implemented by subclasses to do the actual
208         application work.
209 
210         Params:
211             args = parsed command line arguments
212 
213         Returns:
214             status code to return to the OS
215 
216     ***************************************************************************/
217 
218     abstract protected int run ( Arguments args );
219 
220     /***************************************************************************
221 
222         IArgumentsExtExtension methods dummy implementation.
223 
224         These methods are implemented with an "empty" implementation to ease
225         deriving from this class.
226 
227         See IArgumentsExtExtension documentation for more information on how to
228         override these methods.
229 
230     ***************************************************************************/
231 
232     override public void setupArgs ( IApplication app, Arguments args )
233     {
234         // Dummy implementation of the interface
235     }
236 
237     override public void preValidateArgs ( IApplication app, Arguments args )
238     {
239         // Dummy implementation of the interface
240     }
241 
242     override public cstring validateArgs ( IApplication app, Arguments args )
243     {
244         // Dummy implementation of the interface
245         return null;
246     }
247 
248     override public void processArgs ( IApplication app, Arguments args )
249     {
250         // Dummy implementation of the interface
251     }
252 }
253 
254 ///
255 unittest
256 {
257     /***************************************************************************
258 
259         Example CLI application class.
260 
261     ***************************************************************************/
262 
263     class MyApp : CliApp
264     {
265         this ( )
266         {
267             // The name of your app and a short description of what it does.
268             istring name = "my_app";
269             istring desc = "Dummy app for unittest.";
270 
271             // The version info for your app. Normally you get this by importing
272             // Version and passing the AA which contains the version info
273             // (called Version) to CliApp's constructor.
274             auto ver = VersionInfo.init;
275 
276             // You may also pass an instance of OptionalSettings to CliApp's
277             // constructor, to specify non-mandatory options. In this example,
278             // we specify the help text.
279             CliApp.OptionalSettings settings;
280             settings.help = "Actually, this program does nothing. Sorry!";
281 
282             // Call the super class' ctor.
283             super(name, desc, ver, settings);
284         }
285 
286         // Called after arguments and config file parsing.
287         override protected int run ( Arguments args )
288         {
289             // Application main logic.
290 
291             return 0; // return code to OS
292         }
293     }
294 
295     /***************************************************************************
296 
297         Your application's main() function should look something like this.
298         (This function is not called here as we don't want to actually run the
299         application in this unittest.)
300 
301     ***************************************************************************/
302 
303     int main ( istring[] cl_args )
304     {
305         // Instantiate an instance of your app class.
306         auto my_app = new MyApp;
307 
308         // Pass the raw command line arguments to its main function.
309         auto ret = my_app.main(cl_args);
310 
311         // Return ret to the OS.
312         return ret;
313     }
314 }
315