reworked handling of implicit options...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2020-08-02 19:18:44 +03:00
parent 6c2df284b2
commit ed23022e83
5 changed files with 56 additions and 24 deletions

View File

@ -98,7 +98,8 @@ Parser(..) -> <parser>(..) -> <parsed>
<parser>(...) <parser>(...)
-> <parsed> -> <parsed>
``` ```
- option handlers (defined in `<spec>`) are called while parsing, - arguments are handled in order of occurrence,
- argument handlers (defined in `<spec>`) are called while parsing,
- then/stop/error `<callback>`'s are called after the `<parser>` is done, - then/stop/error `<callback>`'s are called after the `<parser>` is done,
- everything is run in the _context_ of the `<parsed>` object so any - everything is run in the _context_ of the `<parsed>` object so any
data set on it is accessible after parsing is done for further data set on it is accessible after parsing is done for further
@ -218,18 +219,22 @@ These, if encountered, simply assign a value to an attribute on the parsed objec
Any option/command can be passed a value, either explicitly (e.g. `-opt=123`) or Any option/command can be passed a value, either explicitly (e.g. `-opt=123`) or
implicitly by first setting `.arg` (see examples below) and and then passing `-opt 123`. implicitly by first setting `.arg` (see examples below) and and then passing `-opt 123`.
If no value is given `true` is assigned to option attribute on the parsed object If option is given but no value is set, `undefined` is assigned to option
to indicate that the option/command is present in the command-line. attribute on the parsed object to indicate that the option/command is present
in the command-line.
Note that values can be set on the command-line as well as via
[`<option>.env`](./ADVANCED.md#optionenv) and/or
[`<option>.default`](./ADVANCED.md#optiondefault) see examples below.
Note that repeating a basic option/command will overwrite the previous value Note that repeating a basic option/command will overwrite the previous occurrences'
unless `.collect` is set (see `-push` example below). value unless `.collect` is set (see `-push` example below).
Note that in general case option order in the command-line is not critical, Note that in the general case option order in the command-line is not critical,
but option context can matter (see: [Active options/commands](#active-optionscommands) and [Nested parsers](#nested-parsers)) but option context can matter (see: [Active options/commands](#active-optionscommands) and [Nested parsers](#nested-parsers))
```javascript ```javascript
'-bool': { '-flag': {
doc: 'if given, set .bool to true' }, doc: 'if given, set .flag' },
// option with a value... // option with a value...
@ -263,6 +268,8 @@ but option context can matter (see: [Active options/commands](#active-optionscom
// NOTE: of no attr is specified in arg option name is used. // NOTE: of no attr is specified in arg option name is used.
arg: '| required_option_given', arg: '| required_option_given',
default: true,
// NOTE: by default required options/commands are sorted above normal // NOTE: by default required options/commands are sorted above normal
// options but bellow -help/-version/-quiet/... // options but bellow -help/-version/-quiet/...
// (by default at priority 80) // (by default at priority 80)
@ -355,6 +362,11 @@ And for quick-n-dirty hacking stuff together, a shorthand (_not for production u
}, },
``` ```
The `.handler(..)` will get called if the option is present in the command-line,
if either `.default` is not `undefined` or if `.env` and its environment
variable are defined, or any combination of the above. And vice-versa, if none
of the above conditions are met the `.handler(..)` will not be called.
Option's `.handler(..)` only sees the `args` that follow it in the command line, Option's `.handler(..)` only sees the `args` that follow it in the command line,
thus anything it may expect/get from the arguments must follow it (in the manner thus anything it may expect/get from the arguments must follow it (in the manner
it expects), `argv.js` poses no restrictions on full or partial manual handling it expects), `argv.js` poses no restrictions on full or partial manual handling
@ -494,7 +506,7 @@ Options:
(required) (required)
--default=VALUE - option with default value --default=VALUE - option with default value
(default: some value) (default: some value)
--bool - if given set .bool to true --flag - if given set .flag
--value=X - set .x to X --value=X - set .x to X
(required value) (required value)
-i=INT - pass an integer value -i=INT - pass an integer value

33
argv.js
View File

@ -463,9 +463,9 @@ object.Constructor('Parser', {
// delegate option processing to a different option. // delegate option processing to a different option.
// (see '-?' for a usage example) // (see '-?' for a usage example)
// NOTE: this will not handle anything outside of handler call // NOTE: this will not handle anything outside of handler call
handle: function(handler, rest, key, value){ handle: function(handler, rest, key, value, mode){
// got flag as handler... // got flag as handler...
[key, handler] = ;[key, handler] =
typeof(handler) == typeof('str') ? typeof(handler) == typeof('str') ?
this.handler(handler) this.handler(handler)
: [key, handler] : [key, handler]
@ -748,7 +748,8 @@ object.Constructor('Parser', {
'-q': '-quiet', '-q': '-quiet',
'-quiet': { '-quiet': {
priority: 99, priority: 99,
doc: 'quiet mode', }, doc: 'quiet mode',
default: true, },
// Stop argument processing... // Stop argument processing...
@ -865,11 +866,8 @@ object.Constructor('Parser', {
// get the final key... // get the final key...
|| this.handler(key)[0].slice(1) || this.handler(key)[0].slice(1)
// if value not given set true and handle... // if value not given set true and handle...
//this[key] = arguments.length < 3 ? value = this.handleArgumentValue ?
value = arguments.length < 3 ? this.handleArgumentValue(handler, value)
(this.handleArgumentValue ?
this.handleArgumentValue(handler, true)
: true)
: value : value
this[attr] = this._handleValue(handler, this[attr] = this._handleValue(handler,
@ -981,18 +979,22 @@ object.Constructor('Parser', {
parsed.error(reason, arg, rest) parsed.error(reason, arg, rest)
parsed.handleErrorExit parsed.handleErrorExit
&& parsed.handleErrorExit(arg, reason) } && parsed.handleErrorExit(arg, reason) }
var runHandler = function(handler, arg, rest){ var runHandler = function(handler, arg, rest, mode){
var [arg, value] = arg instanceof Array ? var [arg, value] = arg instanceof Array ?
arg arg
: arg.split(/=/) : arg.split(/=/)
var env = handler.env
&& handler.env.replace(/^\$/, '')
// get value... // get value...
value = value == null ? value = value == null ?
((parsed.hasArgument(handler) ((parsed.hasArgument(handler)
&& rest.length > 0 && rest.length > 0
&& !opt_pattern.test(rest[0])) ? && !opt_pattern.test(rest[0])) ?
rest.shift() rest.shift()
: (typeof(process) != 'undefined' && handler.env) ? : (typeof(process) != 'undefined'
process.env[handler.env.replace(/^\$/, '')] && env
&& env in process.env) ?
process.env[env]
: value) : value)
: value : value
value = value == null ? value = value == null ?
@ -1007,6 +1009,11 @@ object.Constructor('Parser', {
if(handler.valueRequired && value == null){ if(handler.valueRequired && value == null){
throw module.ParserValueError('Value missing: ${ arg }=?') } throw module.ParserValueError('Value missing: ${ arg }=?') }
// do not call the handler if value is implicitly undefined...
if(value === undefined
&& mode == 'implicit'){
return }
// run handler... // run handler...
try { try {
var res = parsed.handle(handler, rest, arg, value) var res = parsed.handle(handler, rest, arg, value)
@ -1056,6 +1063,7 @@ object.Constructor('Parser', {
while(rest.length > 0 || (values.size || values.length) > 0){ while(rest.length > 0 || (values.size || values.length) > 0){
// explicitly passed options... // explicitly passed options...
if(rest.length > 0){ if(rest.length > 0){
var mode = 'explicit'
var arg = rest.shift() var arg = rest.shift()
// non-string stuff in arg list... // non-string stuff in arg list...
if(typeof(arg) != typeof('str')){ if(typeof(arg) != typeof('str')){
@ -1094,13 +1102,14 @@ object.Constructor('Parser', {
// implicit options -- with .env and or .default set... // implicit options -- with .env and or .default set...
} else { } else {
var mode = 'implicit'
values = values instanceof Map ? values = values instanceof Map ?
[...values] [...values]
: values : values
var [handler, arg] = values.shift() } var [handler, arg] = values.shift() }
var res = runHandler(handler, arg, rest) var res = runHandler(handler, arg, rest, mode)
// handle stop conditions... // handle stop conditions...
if(res === module.STOP if(res === module.STOP

View File

@ -16,8 +16,8 @@ argv.Parser({
footer: 'Written by: $AUTHOR\nVersion: $VERSION / License: $LICENSE', footer: 'Written by: $AUTHOR\nVersion: $VERSION / License: $LICENSE',
'-bool': { '-flag': {
doc: 'if given, set .bool to true' }, doc: 'if given, set .bool' },
// option with a value... // option with a value...
@ -48,6 +48,8 @@ argv.Parser({
// NOTE: of no attr is specified in arg option name is used. // NOTE: of no attr is specified in arg option name is used.
arg: '| required_option_given', arg: '| required_option_given',
default: true,
// NOTE: by default required options/commands are sorted above normal // NOTE: by default required options/commands are sorted above normal
// options but bellow -help/-version/-quiet/... // options but bellow -help/-version/-quiet/...
// (by default at priority 80) // (by default at priority 80)

View File

@ -1,6 +1,6 @@
{ {
"name": "ig-argv", "name": "ig-argv",
"version": "2.10.4", "version": "2.11.0",
"description": "simple argv parser", "description": "simple argv parser",
"main": "argv.js", "main": "argv.js",
"scripts": { "scripts": {

View File

@ -89,6 +89,15 @@ argv.Parser({
'-sh': { '-sh': {
doc: 'short option', }, doc: 'short option', },
'-env': {
doc: 'env value',
arg: 'VALUE | env_value',
env: 'VALUE',
//default: 5,
handler: function(args, key, value){ console.log('GOT ENV:', value) },
},
'-type-error': { '-type-error': {
doc: 'throw a type error', doc: 'throw a type error',
type: 'error', type: 'error',