1 /** 2 * Program and command entries. 3 * 4 * This module contains interfaces and implementations of various entries. 5 * Generally, most APIs expose builder-like pattern, where setters return 6 * class instance to allow chaining. 7 * 8 * Entries are all things that can be added to program or command - that is 9 * flags, options and arguments. All entries contain a name - which is an unique 10 * identifier for every entry. Names must be a valid alpha numeric identifier. 11 * 12 * Result of parsing arguments (instance of `ProgramArgs`) allows reading 13 * argument values by entry `name` (not by `-short` or `--long-forms`). 14 * 15 * See_Also: 16 * Flag, Option, Argument 17 */ 18 module commandr.option; 19 20 import commandr.validators; 21 import commandr.program : InvalidProgramException; 22 23 24 /** 25 * Interface for all program or command entries - flags, options and arguments. 26 */ 27 interface IEntry { 28 /** 29 * Sets entry name. 30 */ 31 public typeof(this) name(string name) pure @safe; 32 33 /** 34 * Entry name. 35 */ 36 public string name() const pure nothrow @safe; 37 38 /** 39 * Display name for entry. 40 * 41 * For arguments, this is argument tag value. 42 * For options and flags, this is either abbrevation with single dash prefix 43 * or full name with double dash prefix. 44 */ 45 public string displayName() const pure nothrow @safe; 46 47 /** 48 * Sets entry help description (one-liner). 49 */ 50 public typeof(this) description(string description) pure @safe; 51 52 /** 53 * Entry help description (one-liner). 54 */ 55 public string description() const pure nothrow @safe @nogc; 56 57 /** 58 * Sets whenever entry can be repeated. 59 */ 60 public typeof(this) repeating(bool repeating = true) pure @safe; 61 62 /** 63 * Gets whenever entry can be repeated. 64 */ 65 public bool isRepeating() const pure nothrow @safe @nogc; 66 67 /** 68 * Sets entry required flag. 69 */ 70 public typeof(this) required(bool required = true) pure @safe; 71 72 /** 73 * Sets entry optional flag. 74 */ 75 public typeof(this) optional(bool optional = true) pure @safe; 76 77 /** 78 * Whenever entry is required. 79 */ 80 public bool isRequired() const pure nothrow @safe @nogc; 81 82 /** 83 * Sets entry default value. 84 */ 85 public typeof(this) defaultValue(string defaultValue) pure @safe; 86 87 /** 88 * Sets entry default value array. 89 */ 90 public typeof(this) defaultValue(string[] defaultValue) pure @safe; 91 92 /** 93 * Entry default value array. 94 */ 95 public string[] defaultValue() pure nothrow @safe @nogc; 96 97 /** 98 * Adds entry validator. 99 */ 100 public typeof(this) validate(IValidator validator) pure @safe; 101 102 /** 103 * Entry validators. 104 */ 105 public IValidator[] validators() pure nothrow @safe @nogc; 106 } 107 108 mixin template EntryImpl() { 109 private string _name; 110 private string _description; 111 private bool _repeating = false; 112 private bool _required = false; 113 private string[] _default; 114 private IValidator[] _validators; 115 116 /// 117 public typeof(this) name(string name) pure nothrow @safe @nogc { 118 this._name = name; 119 return this; 120 } 121 122 /// 123 public string name() const pure nothrow @safe @nogc { 124 return this._name; 125 } 126 127 /// 128 public typeof(this) description(string description) pure nothrow @safe @nogc { 129 this._description = description; 130 return this; 131 } 132 133 /// 134 public string description() const pure nothrow @safe @nogc { 135 return this._description; 136 } 137 138 /// 139 public typeof(this) repeating(bool repeating = true) pure nothrow @safe @nogc { 140 this._repeating = repeating; 141 return this; 142 } 143 144 /// 145 public bool isRepeating() const pure nothrow @safe @nogc { 146 return this._repeating; 147 } 148 149 /// 150 public typeof(this) required(bool required = true) pure @safe { 151 this._required = required; 152 153 return this; 154 } 155 156 /// 157 public typeof(this) optional(bool optional = true) pure @safe { 158 this.required(!optional); 159 return this; 160 } 161 162 /// 163 public bool isRequired() const pure nothrow @safe @nogc { 164 return this._required; 165 } 166 167 /// 168 public typeof(this) defaultValue(string defaultValue) pure @safe { 169 return this.defaultValue([defaultValue]); 170 } 171 172 /// 173 public typeof(this) defaultValue(string[] defaultValue) pure @safe { 174 this._default = defaultValue; 175 this._required = false; 176 return this; 177 } 178 179 /// 180 public string[] defaultValue() pure nothrow @safe @nogc { 181 return this._default; 182 } 183 184 /// 185 public typeof(this) validate(IValidator validator) pure @safe { 186 this._validators ~= validator; 187 return this; 188 } 189 190 /// 191 public IValidator[] validators() pure nothrow @safe @nogc { 192 return this._validators; 193 } 194 } 195 196 /** 197 * Option interface. 198 * 199 * Used by flags and options, which both contain short and long names. 200 * Either can be null but not both. 201 */ 202 interface IOption: IEntry { 203 /** 204 * Sets option full name (long-form). 205 * 206 * Set to null to disable long form. 207 */ 208 public typeof(this) full(string full) pure nothrow @safe @nogc; 209 210 /** 211 * Option full name (long-form). 212 */ 213 public string full() const pure nothrow @safe @nogc; 214 215 /// ditto 216 public alias long_ = full; 217 218 /** 219 * Sets option abbrevation (short-form). 220 * 221 * Set to null to disable short form. 222 */ 223 public typeof(this) abbrev(string abbrev) pure nothrow @safe @nogc; 224 225 /** 226 * Sets option abbrevation (short-form). 227 */ 228 public string abbrev() const pure nothrow @safe @nogc; 229 230 /// ditto 231 public alias short_ = abbrev; 232 } 233 234 mixin template OptionImpl() { 235 private string _abbrev; 236 private string _full; 237 238 /// 239 public string displayName() const nothrow pure @safe { 240 if (_abbrev) { 241 return "-" ~ _abbrev; 242 } 243 return "--" ~ _full; 244 } 245 246 /// 247 public typeof(this) full(string full) pure nothrow @safe @nogc { 248 this._full = full; 249 return this; 250 } 251 252 /// 253 public string full() const pure nothrow @safe @nogc { 254 return this._full; 255 } 256 257 /// 258 public alias long_ = full; 259 260 /// 261 public typeof(this) abbrev(string abbrev) pure nothrow @safe @nogc { 262 this._abbrev = abbrev; 263 return this; 264 } 265 266 /// 267 public string abbrev() const pure nothrow @safe @nogc { 268 return this._abbrev; 269 } 270 271 /// 272 public alias short_ = abbrev; 273 } 274 275 276 /** 277 * Represents a flag. 278 * 279 * Flag hold a single boolean value. 280 * Flags are optional and cannot be set as required. 281 */ 282 public class Flag: IOption { 283 mixin EntryImpl; 284 mixin OptionImpl; 285 286 /** 287 * Creates new flag. 288 * 289 * Full flag name (long-form) is set to name parameter value. 290 * 291 * Params: 292 * name - flag unique name. 293 */ 294 public this(string name) pure nothrow @safe @nogc { 295 this._name = name; 296 this._full = name; 297 } 298 299 300 /** 301 * Creates new flag. 302 * 303 * Name defaults to long form value. 304 * 305 * Params: 306 * abbrev - Flag short name (null for none) 307 * full - Flag full name (null for none) 308 * description - Flag help description 309 */ 310 public this(string abbrev, string full, string description) pure nothrow @safe @nogc { 311 this._name = full; 312 this._full = full; 313 this._abbrev = abbrev; 314 this._description = description; 315 } 316 } 317 318 /** 319 * Represents an option. 320 * 321 * Options hold any value as string (or array of strings). 322 * Options by default are optional, but can be marked as required. 323 * 324 * Order in which options are passed does not matter. 325 */ 326 public class Option: IOption { 327 mixin OptionImpl; 328 mixin EntryImpl; 329 330 private string _tag = "value"; 331 332 333 /** 334 * Creates new option. 335 * 336 * Full option name (long-form) is set to `name` parameter value. 337 * 338 * Params: 339 * name - option unique name. 340 */ 341 public this(string name) pure nothrow @safe @nogc { 342 this._name = name; 343 this._full = name; 344 } 345 346 /** 347 * Creates new option. 348 * 349 * Name defaults to long form value. 350 * 351 * Params: 352 * abbrev - Option short name (null for none) 353 * full - Option full name (null for none) 354 * description - Option help description 355 */ 356 public this(string abbrev, string full, string description) pure nothrow @safe @nogc { 357 this._name = full; 358 this._full = full; 359 this._abbrev = abbrev; 360 this._description = description; 361 } 362 363 /** 364 * Sets option value tag. 365 * 366 * A tag is a token displayed in place of option value. 367 * Default tag is `value`. 368 * 369 * For example, for a option that takes path to configuration file, 370 * one can create `--config` option and set `tag` to `config-path`, so that in 371 * help it is displayed as `--config=config-path` instead of `--config=value` 372 */ 373 public typeof(this) tag(string tag) pure nothrow @safe @nogc { 374 this._tag = tag; 375 return this; 376 } 377 378 /** 379 * Option value tag. 380 */ 381 public string tag() const pure nothrow @safe @nogc { 382 return this._tag; 383 } 384 } 385 386 /** 387 * Represents an argument. 388 * 389 * Arguments are positional parameters passed to program and are required by default. 390 * Only last argument can be repeating or optional. 391 */ 392 public class Argument: IEntry { 393 mixin EntryImpl; 394 395 private string _tag; 396 397 /** 398 * Creates new argument. 399 * 400 * Params: 401 * name - Argument name 402 * description - Help description 403 */ 404 public this(string name, string description = null) nothrow pure @safe @nogc { 405 this._name = name; 406 this._description = description; 407 this._required = true; 408 this._tag = name; 409 } 410 411 /** 412 * Gets argument display name (tag or name). 413 */ 414 public string displayName() const nothrow pure @safe { 415 return this._tag; 416 } 417 418 /** 419 * Sets argument tag. 420 * 421 * A tag is a token displayed in place of argument. 422 * By default it is name of the argument. 423 */ 424 public typeof(this) tag(string tag) pure nothrow @safe @nogc { 425 this._tag = tag; 426 return this; 427 } 428 429 /** 430 * Argument tag 431 */ 432 public string tag() const pure nothrow @safe @nogc { 433 return this._tag; 434 } 435 } 436 437 /** 438 * Thrown when user-passed data is invalid. 439 * 440 * This exception is thrown during parsing phase when user passed arguments (e.g. invalid option, invalid value). 441 * 442 * This exception is automatically caught if using `parse` function. 443 */ 444 public class InvalidArgumentsException: Exception { 445 /** 446 * Creates new InvalidArgumentException 447 */ 448 public this(string msg) nothrow pure @safe @nogc { 449 super(msg); 450 } 451 } 452 453 // options 454 unittest { 455 assert(!new Option("t", "test", "").isRequired); 456 } 457 458 // arguments 459 unittest { 460 assert(new Argument("test", "").isRequired); 461 }