1 /******************************************************************************* 2 3 Application extension to parse configuration files. 4 5 Copyright: 6 Copyright (c) 2009-2016 dunnhumby Germany GmbH. 7 All rights reserved. 8 9 License: 10 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. 11 Alternatively, this file may be distributed under the terms of the Tango 12 3-Clause BSD License (see LICENSE_BSD.txt for details). 13 14 *******************************************************************************/ 15 16 module ocean.util.app.ext.ConfigExt; 17 18 19 20 import ocean.util.app.model.ExtensibleClassMixin; 21 import ocean.util.app.model.IApplicationExtension; 22 import ocean.util.app.Application; 23 import ocean.util.app.ext.model.IConfigExtExtension; 24 import ocean.util.app.ext.model.IArgumentsExtExtension; 25 import ocean.util.app.ext.ArgumentsExt; 26 27 import ocean.util.config.ConfigParser; 28 import ocean.text.Arguments; 29 import ocean.io.Stdout : Stderr; 30 31 import ocean.meta.types.Qualifiers; 32 import ocean.core.Verify; 33 import ocean.text.Util : join, locate, locatePrior, trim; 34 import ocean.core.ExceptionDefinitions : IOException; 35 36 37 38 /******************************************************************************* 39 40 Application extension to parse configuration files. 41 42 This extension is an extension itself, providing new hooks via 43 IConfigExtExtension. 44 45 It is also an extension for the ArgumentsExt extension, so if it is 46 registered as such, it will add the --config command line option to specify 47 the configuration file to read. If loose_config_parsing is false, it will 48 also add a --loose-config-parsing option to enable that feature. 49 50 *******************************************************************************/ 51 52 class ConfigExt : IApplicationExtension, IArgumentsExtExtension 53 { 54 import ConfigOverrides = ocean.application.components.ConfigOverrides; 55 56 /*************************************************************************** 57 58 Adds a list of extensions (this.extensions) and methods to handle them. 59 See ExtensibleClassMixin documentation for details. 60 61 ***************************************************************************/ 62 63 mixin ExtensibleClassMixin!(IConfigExtExtension); 64 65 66 /*************************************************************************** 67 68 Configuration parser to use. 69 70 ***************************************************************************/ 71 72 public ConfigParser config; 73 74 75 /*************************************************************************** 76 77 If true, configuration files will be parsed in a more relaxed way. 78 79 This might be overridden by command line arguments. 80 81 ***************************************************************************/ 82 83 public bool loose_config_parsing; 84 85 86 /*************************************************************************** 87 88 Default configuration files to parse. 89 90 ***************************************************************************/ 91 92 public istring[] default_configs; 93 94 95 /*************************************************************************** 96 97 Constructor. 98 99 Params: 100 loose_config_parsing = if true, configuration files will be parsed 101 in a more relaxed way 102 default_configs = default configuration files to parse 103 config = configuration parser to use, instantiate one if null 104 is passed 105 106 ***************************************************************************/ 107 108 this ( bool loose_config_parsing = false, 109 istring[] default_configs = [ "etc/config.ini" ], 110 ConfigParser config = null ) 111 { 112 this.loose_config_parsing = loose_config_parsing; 113 this.default_configs = default_configs; 114 if ( config is null ) 115 { 116 config = new ConfigParser; 117 } 118 this.config = config; 119 } 120 121 122 /*************************************************************************** 123 124 Extension order. This extension uses -10_000 because it should be 125 called pretty early, but after the ArgumentsExt extension. 126 127 Returns: 128 the extension order 129 130 ***************************************************************************/ 131 132 public override int order ( ) 133 { 134 return -10_000; 135 } 136 137 138 /*************************************************************************** 139 140 Setup command line arguments. 141 142 Adds the following additional command line arguments: 143 --config/-c 144 --loose-config-parsing (if needed) 145 --override-config/-O 146 147 Params: 148 app = the application instance 149 args = parsed command line arguments 150 151 ***************************************************************************/ 152 153 public override void setupArgs ( IApplication app, Arguments args ) 154 { 155 args("config").aliased('c').params(1).smush() 156 .help("use the given configuration file"); 157 158 foreach (conf; this.default_configs) 159 { 160 args("config").defaults(conf); 161 } 162 163 if (!this.loose_config_parsing) 164 { 165 args("loose-config-parsing").params(0) 166 .help("ignore unknown configuration parameters in config file"); 167 } 168 169 ConfigOverrides.setupArgs(args); 170 } 171 172 173 /*************************************************************************** 174 175 Process command line arguments (ArgumentsExt hook). 176 177 Overrides the loose_config_parsing variable if appropriate. 178 179 Params: 180 app = the application instance 181 args = parsed command line arguments 182 183 ***************************************************************************/ 184 185 public override void processArgs ( IApplication app, Arguments args ) 186 { 187 if (!this.loose_config_parsing) 188 { 189 this.loose_config_parsing = args("loose-config-parsing").set; 190 } 191 } 192 193 194 /*************************************************************************** 195 196 Do a simple validation over override-config arguments 197 198 Params: 199 app = the application instance 200 args = parsed command line arguments 201 202 Returns: 203 error text if any 204 205 ***************************************************************************/ 206 207 public override cstring validateArgs ( IApplication app, Arguments args ) 208 { 209 return ConfigOverrides.validateArgs(args); 210 } 211 212 213 /*************************************************************************** 214 215 Parse configuration files (Application hook). 216 217 This function do all the extension processing invoking all the 218 extensions hooks. 219 220 If configuration file parsing fails, it exits with status code 3 and 221 prints an appropriate error message. 222 223 Note: 224 This is not done in processArgs() method because it can be used 225 without being registered as a ArgumentsExt extension. 226 227 Params: 228 app = the application instance 229 cl_args = command line arguments 230 231 ***************************************************************************/ 232 233 public override void preRun ( IApplication app, istring[] cl_args ) 234 { 235 foreach (ext; this.extensions) 236 { 237 ext.preParseConfig(app, this.config); 238 } 239 240 auto args_ext = (cast(Application)app).getExtension!(ArgumentsExt); 241 // If an ArgumentExt is present, `.assigned` returns the user's 242 // input or the default 243 auto config_files = (args_ext !is null) 244 ? args_ext.args("config").assigned.dup : this.default_configs; 245 246 foreach (e; this.extensions) 247 { 248 config_files = e.filterConfigFiles(app, this.config, config_files); 249 } 250 251 foreach (config_file; config_files) 252 { 253 try 254 { 255 this.config.parseFile(config_file, false); 256 } 257 catch (IOException e) 258 { 259 app.exit(3, "Error reading config file '" ~ config_file ~ 260 "': " ~ idup(e.message())); 261 } 262 } 263 264 if (args_ext !is null) 265 { 266 ConfigOverrides.handleArgs(args_ext.args, this.config); 267 } 268 269 foreach (ext; this.extensions) 270 { 271 ext.processConfig(app, this.config); 272 } 273 } 274 275 276 /*************************************************************************** 277 278 Unused IApplicationExtension method. 279 280 We just need to provide an "empty" implementation to satisfy the 281 interface. 282 283 ***************************************************************************/ 284 285 public override void postRun ( IApplication app, istring[] args, int status ) 286 { 287 // Unused 288 } 289 290 /// ditto 291 public override void atExit ( IApplication app, istring[] args, int status, 292 ExitException exception ) 293 { 294 // Unused 295 } 296 297 /// ditto 298 public override ExitException onExitException ( IApplication app, 299 istring[] args, 300 ExitException exception ) 301 { 302 // Unused 303 return exception; 304 } 305 306 307 /*************************************************************************** 308 309 Unused IArgumentsExtExtension method. 310 311 We just need to provide an "empty" implementation to satisfy the 312 interface. 313 314 ***************************************************************************/ 315 316 public override void preValidateArgs ( IApplication app, Arguments args ) 317 { 318 // Unused 319 } 320 }