Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Documented narwhal/args.

Added documentation for:
- Parser (option, group, command, arg, args, def,
    interleaved, act, action, helpful, end, reset,
    parse)
- Argument (name, optional)
- Group (option, group, end)
- Option (_, __, name, displayName, getDisplayName,
    getName, action, set, push, inc, dec, choices,
    validate, input, output, number, oct, hex, def,
    integer, natural, whole, bool, inverse, help,
    halt, hidden, end, option)
  • Loading branch information...
commit 6cdc22aa5c1c36bd89c6223073296ca372cb38ce 1 parent 66fbfdd
@abhinav abhinav authored
Showing with 387 additions and 3 deletions.
  1. +387 −3 packages/narwhal-lib/lib/narwhal/args.js
View
390 packages/narwhal-lib/lib/narwhal/args.js
@@ -23,6 +23,11 @@ exports.ConfigurationError = function (message) {
exports.ConfigurationError.prototype = Object.create(Error.prototype);
+/**
+ * Create a new command line argument parser.
+ *
+ * @constructor
+ */
exports.Parser = function () {
this._options = [];
this._def = {};
@@ -34,23 +39,50 @@ exports.Parser = function () {
this._interleaved = false;
};
+/**
+ * Add an option to the parser.
+ *
+ * Takes the same arguments as the {@link Option} constructor.
+ *
+ * @returns {Option} the new Option object
+ */
exports.Parser.prototype.option = function () {
var option = new this.Option(this, arguments);
this._options.push(option);
return option;
};
+/**
+ * Create a new group of options.
+ *
+ * @param {String} name name of the group
+ * @returns {Group} {@link Group} object representing the group
+ */
exports.Parser.prototype.group = function (name) {
var group = new this.Group(this, this, name);
this._options.push(group);
return group;
};
+/**
+ * Set default values for the parser.
+ *
+ * @param {String} name key in the result
+ * @param value default value for the key
+ * @returns {Parser} this
+ */
exports.Parser.prototype.def = function (name, value) {
this._def[name] = value;
return this;
};
+/**
+ * Reset the default values in the given hash.
+ *
+ * Normally, this won't be used externally.
+ *
+ * @param {Object} options parser state
+ */
exports.Parser.prototype.reset = function (options) {
for (var name in this._def) {
if (util.has(this._def, name) && !util.has(options, name))
@@ -64,6 +96,16 @@ exports.Parser.prototype.reset = function (options) {
});
};
+/**
+ * Add a new sub-command to the parser.
+ *
+ * @param {String} name name of the sub command
+ * @param [handler] either a module name that exports a `parser'
+ * or a function that will be used as a parser
+ * action
+ * @returns {Parser} if no handler was given or a parser action was
+ * given, returns the Parser for the sub-command
+ */
exports.Parser.prototype.command = function (name, handler) {
var parent = this;
if (!handler) {
@@ -87,18 +129,44 @@ exports.Parser.prototype.command = function (name, handler) {
}
};
+/**
+ * Add a single positional argument to the command.
+ *
+ * Warning: Only used for printing the help or usage. The parser is not
+ * responsible for reading them from the command line arguments.
+ *
+ * @param {String} name name of the argument
+ * @returns {Argument} {@link Argument} object reprsenting the argument
+ */
exports.Parser.prototype.arg = function (name) {
var argument = new exports.Argument(this).name(name);
this._args.push(argument);
return argument;
};
+/**
+ * Add a variable number of arguments to the command.
+ *
+ * Warning: Only used for printing the help or usage. The parser is not
+ * responsible for reading them from the command line arguments.
+ *
+ * @param {String} name name of the arguments
+ * @returns {Argument} {@link Argument} object representing the argument
+ */
exports.Parser.prototype.args = function (name) {
var argument = new exports.Argument(this).name(name);
this._vargs = argument;
return argument;
};
+/**
+ * Enable or disable interleaved arguments.
+ *
+ * Disabled by default.
+ *
+ * @param {Boolean} [value=true] true to allow interleaved arguments
+ * @returns {Parser} this
+ */
exports.Parser.prototype.interleaved = function (value) {
if (value === undefined)
value = true;
@@ -106,6 +174,16 @@ exports.Parser.prototype.interleaved = function (value) {
return this;
};
+/**
+ * Act on the given arguments.
+ *
+ * Parses the arguments and calls the appropriate actions.
+ *
+ * Normally, this won't be used externally.
+ *
+ * @param {String[]} args arguments to parse
+ * @param {Option[]} options result of the parent parser
+ */
exports.Parser.prototype.act = function (args, options) {
if (!this._action) {
this.error(options, "Not yet implemented.");
@@ -115,6 +193,18 @@ exports.Parser.prototype.act = function (args, options) {
this._action.call(this, this.parse(args), options);
};
+/**
+ * Add an action to the parser.
+ *
+ * If an action already exists, the new action will be executed after the
+ * executing action.
+ *
+ * Warning: Not executed when the parse method is called. Normally used on
+ * sub-command parsers only.
+ *
+ * @param {Function} action the action to execute
+ * @returns {Parser} this
+ */
exports.Parser.prototype.action = function (action) {
if (this._action) {
action = (function (previous, next) {
@@ -128,7 +218,15 @@ exports.Parser.prototype.action = function (action) {
return this;
};
-// should be called last
+/**
+ * Make the parser helpful.
+ *
+ * Will add help options and if required, commands.
+ *
+ * Warning: Must be called last, after all parser configuration is finished.
+ *
+ * @returns {Parser} this
+ */
exports.Parser.prototype.helpful = function () {
var self = this;
this.option('-h', '--help')
@@ -338,9 +436,23 @@ exports.Parser.prototype.check = function () {
});
};
-// TODO break this into sub-functions
-// TODO wrap with a try catch and print the progress through the arguments
+/**
+ * Parse the arguments, calling the appropriate option actions.
+ *
+ * @param {String[]} [args=system.args] command line arguments
+ * @param {Object} [options] parser state
+ * @param {Boolean} [noCommand=false] true if sub-commands are not
+ * allowed
+ * @param {Boolean} [allowInterleaved] true to allow interleaved
+ * arguments; overrides
+ * this.interleaved
+ * @returns {Object} final parser state
+ */
exports.Parser.prototype.parse = function (args, options, noCommand, allowInterleaved) {
+
+ // TODO break this into sub-functions
+ // TODO wrap with a try catch and print the progress through the arguments
+
var self = this;
this.check();
@@ -502,16 +614,34 @@ exports.Parser.prototype.parse = function (args, options, noCommand, allowInterl
return options;
};
+/**
+ * Represents positional arguments for the parser.
+ *
+ * @constructor
+ * @param {Parser} parser the parent parser
+ */
exports.Argument = function (parser) {
this._parser = parser;
return this;
};
+/**
+ * Set the name of the argument.
+ *
+ * @param {String} name name of the parser
+ * @returns {Argument} this
+ */
exports.Argument.prototype.name = function (name) {
this._name = name;
return this;
};
+/**
+ * Make the argument optional.
+ *
+ * @param {Boolean} [value=true] true to make this optional
+ * @returns {Argument} this
+ */
exports.Argument.prototype.optional = function (value) {
if (value === undefined)
value = true;
@@ -519,6 +649,45 @@ exports.Argument.prototype.optional = function (value) {
return this;
};
+/**
+ * Represents a command line option.
+ *
+ * Other than the parser, the arguments are read with the following rules.
+ *
+ * Hashes contain attributes.
+ * <code>
+ * new Option(parser, {
+ * action: function () { ... },
+ * _: 'l', // short name
+ * __: 'list', // long name
+ * help: "list all packages"
+ * });
+ * </code>
+ *
+ * A function is the option's action.
+ * <code>
+ * new Option(parser, function () { ... });
+ * </code>
+ *
+ * Strings starting with "-" and "--" are short and long names respectivey.
+ * <code>
+ * new Option(parser, "-l", "--list");
+ * </code>
+ *
+ * A string with spaces is the help message.
+ * <code>
+ * new Option(parser, "-l", "--list", "list all packages");
+ * </code>
+ *
+ * A one-word string is the display name and the option name. An additional
+ * one-word string is the option name.
+ * <code>
+ * new Option(parser, "-d", "--delete", "file", "del");
+ * // file is the display name and del is the option name
+ * </code>
+ *
+ * @param {Parser} parser the owning parser
+ */
exports.Option = function (parser, args) {
var self = this;
this._parser = parser;
@@ -559,34 +728,69 @@ exports.Option = function (parser, args) {
return this;
};
+/**
+ * Set the short option.
+ *
+ * @param {String} letter the character for the option
+ * @returns {Option} this
+ */
exports.Option.prototype._ = function (letter) {
this._short.push(letter);
this._parser._short[letter] = this;
return this;
};
+/**
+ * Set the long option.
+ *
+ * @param {String} word the word for the long option
+ * @returns {Option} this
+ */
exports.Option.prototype.__ = function (word) {
this._long.push(word);
this._parser._long[word] = this;
return this;
};
+/**
+ * Set the name of the option.
+ *
+ * Used in the result hash.
+ *
+ * @param {String} name name of the option
+ * @returns {Option} this
+ */
exports.Option.prototype.name = function (name) {
this._name = name;
return this;
};
+/**
+ * Set the display name for the option.
+ *
+ * Shown in the help as the name of the argument. Useless if the option
+ * doesn't have an argument.
+ *
+ * @param {String} displayName new display name
+ * @returns {Option} this
+ */
exports.Option.prototype.displayName = function (displayName) {
this._displayName = displayName;
return this;
};
+/**
+ * @returns {String} the display name
+ */
exports.Option.prototype.getDisplayName = function () {
if (this._displayName)
return this._displayName;
return this.getName();
};
+/**
+ * @returns {String} the name
+ */
exports.Option.prototype.getName = function () {
if (this._name) {
return this._name;
@@ -600,6 +804,13 @@ exports.Option.prototype.getName = function () {
throw new Error("Programmer error: unnamed option");
};
+/**
+ * Set the action executed when this option is encountered.
+ *
+ * @param action either a function or a string with the name of a
+ * function in the parser
+ * @returns {Option} this
+ */
exports.Option.prototype.action = function (action) {
var self = this;
if (typeof action == "string") {
@@ -610,6 +821,15 @@ exports.Option.prototype.action = function (action) {
return this;
};
+/**
+ * If value is given, the option will not take any arguments and will have the
+ * given value if its flag was passed.
+ *
+ * Otherwise, the option will take a single argument.
+ *
+ * @param value desired value
+ * @returns {Option} this
+ */
exports.Option.prototype.set = function (value) {
var option = this;
if (arguments.length == 0)
@@ -624,6 +844,13 @@ exports.Option.prototype.set = function (value) {
throw new exports.UsageError("Option().set takes 0 or 1 arguments");
};
+/**
+ * The option can have multiple values.
+ *
+ * Each argument for this option will be passed separately.
+ *
+ * @returns {Option} this
+ */
exports.Option.prototype.push = function () {
var option = this;
return this.def([]).action(function (options, name, value) {
@@ -634,18 +861,38 @@ exports.Option.prototype.push = function () {
});
};
+/**
+ * The option will keep track of the number of times the flag was passed in the
+ * arguments.
+ *
+ * @returns {Option} this
+ */
exports.Option.prototype.inc = function () {
return this.def(0).action(function (options, name) {
options[name]++;
});
};
+/**
+ * The option's value will be the negative of number of times its flag was
+ * passed in the arguments.
+ *
+ * @returns {Option} this
+ */
exports.Option.prototype.dec = function () {
return this.def(0).action(function (options, name) {
options[name]--;
});
};
+/**
+ * The option can only have one of the given values.
+ *
+ * @param choices array of string containing the choices or hash whose keys
+ * will be the possible choices and the mapped value will be
+ * the value of the option
+ * @returns {Option} this
+ */
exports.Option.prototype.choices = function (choices) {
this.set();
this._choices = choices;
@@ -677,12 +924,30 @@ exports.Option.prototype.choices = function (choices) {
}
};
+/**
+ * Set the default value for the option.
+ *
+ * Overrides setting from Parser.def().
+ *
+ * @param value new default value
+ * @returns {Option} this
+ */
exports.Option.prototype.def = function (value) {
if (this._def === undefined)
this._def = value;
return this;
};
+/**
+ * Add a validate function to the option.
+ *
+ * The last added validate function is executed first.
+ *
+ * @param {Function} validate the validate function - takes the option's
+ * value and returns a new value or the original
+ * value unchanged; can throw {@link UsageError}
+ * @returns {Option} this
+ */
exports.Option.prototype.validate = function (validate) {
var current = this._validate;
if (this._validate) {
@@ -699,6 +964,13 @@ exports.Option.prototype.validate = function (validate) {
return this;
};
+/**
+ * The option will take an input file.
+ *
+ * If the given file name is "-", stdin is used.
+ *
+ * @returns {Option} this
+ */
exports.Option.prototype.input = function () {
return this.set().validate(function (value) {
if (value == "-")
@@ -708,6 +980,13 @@ exports.Option.prototype.input = function () {
});
};
+/**
+ * The option will take an output file.
+ *
+ * If the given file name is "-", stdout is used.
+ *
+ * @returns {Option} this
+ */
exports.Option.prototype.output = function () {
return this.set().validate(function (value) {
if (value == "-")
@@ -717,6 +996,11 @@ exports.Option.prototype.output = function () {
});
};
+/**
+ * The option will take a number.
+ *
+ * @returns {Option} this
+ */
exports.Option.prototype.number = function () {
return this.set().validate(function (value) {
var result = +value;
@@ -726,6 +1010,11 @@ exports.Option.prototype.number = function () {
});
};
+/**
+ * The option will take an octal value.
+ *
+ * @returns {Option} this
+ */
exports.Option.prototype.oct = function () {
return this.set().validate(function (value) {
var result = parseInt(value, 8);
@@ -735,6 +1024,11 @@ exports.Option.prototype.oct = function () {
});
};
+/**
+ * The option will take a hexadecimal value.
+ *
+ * @returns {Option} this
+ */
exports.Option.prototype.hex = function () {
return this.set().validate(function (value) {
var result = parseInt(value, 16);
@@ -744,6 +1038,11 @@ exports.Option.prototype.hex = function () {
});
};
+/**
+ * The option will take an integer value.
+ *
+ * @returns {Option} this
+ */
exports.Option.prototype.integer = function () {
return this.set().validate(function (value) {
var result = parseInt(value, 10);
@@ -753,6 +1052,11 @@ exports.Option.prototype.integer = function () {
});
};
+/**
+ * The option will take a natural number
+ *
+ * @returns {Option} this
+ */
exports.Option.prototype.natural = function () {
return this.set().validate(function (value) {
var result = value >>> 0;
@@ -762,6 +1066,11 @@ exports.Option.prototype.natural = function () {
});
};
+/**
+ * The option will take a whole number.
+ *
+ * @returns {Option} this
+ */
exports.Option.prototype.whole = function () {
return this.set().validate(function (value) {
var result = value >>> 0;
@@ -771,6 +1080,12 @@ exports.Option.prototype.whole = function () {
});
};
+/**
+ * The option will take a boolean value.
+ *
+ * @param {Boolean} def default value
+ * @returns {Option} this
+ */
exports.Option.prototype.bool = function (def) {
if (def === undefined)
def = true;
@@ -790,6 +1105,11 @@ exports.Option.prototype.todo = function (command, value) {
});
};
+/**
+ * The option will have an inverse option.
+ *
+ * @returns {Option} this
+ */
exports.Option.prototype.inverse = function () {
var args = arguments;
if (!args.length) {
@@ -811,11 +1131,25 @@ exports.Option.prototype.inverse = function () {
return this;
};
+/**
+ * Set the help text for this option.
+ *
+ * @param {String} text the help text
+ * @returns {Option} this
+ */
exports.Option.prototype.help = function (text) {
this._help = text;
return this;
};
+/**
+ * The option is final.
+ *
+ * None of the other options will be parsed after this.
+ *
+ * @param {Boolean} [value=true] true to make this option final
+ * @returns {Option} this
+ */
exports.Option.prototype.halt = function (value) {
if (value == undefined)
value = true;
@@ -823,6 +1157,14 @@ exports.Option.prototype.halt = function (value) {
return this;
};
+/**
+ * The option is hidden.
+ *
+ * It won't be shown in the program usage.
+ *
+ * @param {Boolean} [value=true] true to make this option hidden
+ * @returns {Option} this
+ */
exports.Option.prototype.hidden = function (value) {
if (value === undefined)
value = true;
@@ -830,18 +1172,40 @@ exports.Option.prototype.hidden = function (value) {
return this;
};
+/**
+ * Return the option's owning parser.
+ *
+ * Useful for chaining.
+ *
+ * @returns {Parser} owning parser
+ */
exports.Option.prototype.end = function () {
return this._parser;
};
+/**
+ * Helper function equivalent to end().option(...).
+ */
exports.Option.prototype.option = function () {
return this.end().option.apply(this, arguments);
};
+/**
+ * Return the parser's parent parser.
+ *
+ * @returns {Parser} parent parser
+ */
exports.Parser.prototype.end = function () {
return this._parser;
};
+/**
+ * Represents an option group.
+ *
+ * @param {Parser} parser option parser
+ * @param parent parent parser or group
+ * @param {String} name name of the group
+ */
exports.Group = function (parser, parent, name) {
this._name = name;
this._parser = parser;
@@ -850,6 +1214,13 @@ exports.Group = function (parser, parent, name) {
return this;
};
+/**
+ * Add an option to the group.
+ *
+ * Takes the same arguments as the {@link Option} constructor.
+ *
+ * @returns {Option} the new Option object
+ */
exports.Group.prototype.option = function () {
var option = this._parser.option.apply(this._parser, arguments);
option._group = this;
@@ -857,12 +1228,25 @@ exports.Group.prototype.option = function () {
return option;
};
+/**
+ * Create a sub-group to this group.
+ *
+ * @param {String} name name of the new group
+ * @returns {Group} the new group
+ */
exports.Group.prototype.group = function (name) {
var Group = this.Group || this._parser.Group;
var group = new Group(this._parser, this, name);
return group;
};
+/**
+ * Returns the group's parent group or parser.
+ *
+ * Useful for chaining commands.
+ *
+ * @returns parent parser or group
+ */
exports.Group.prototype.end = function () {
return this._parent;
};
Please sign in to comment.
Something went wrong with that request. Please try again.