added THEN stop + some refactoring....

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2020-07-02 02:17:28 +03:00
parent f44ed02a46
commit bad2ea8c6d
2 changed files with 72 additions and 37 deletions

108
argv.js
View File

@ -28,10 +28,14 @@ var object = require('ig-object')
//--------------------------------------------------------------------- //---------------------------------------------------------------------
module.STOP = module.STOP =
{doc: 'stop option processing'} {doc: 'stop option processing, triggers .stop(..) handlers'}
// XXX rename???
module.THEN =
{doc: 'break option processing, triggers .then(..) handlers'}
module.ERROR = module.ERROR =
{doc: 'option processing error'} {doc: 'option processing error, triggers .error(..) handlers'}
@ -79,8 +83,14 @@ var afterCallback = function(name){
// '-t': '-test', // '-t': '-test',
// '-test': { // '-test': {
// doc: 'test option.', // doc: 'test option.',
//
// arg: 'VALUE', // arg: 'VALUE',
//
// env: 'VALUE', // env: 'VALUE',
//
// // XXX
// default: 123,
//
// handler: function(opts, key, value){ // handler: function(opts, key, value){
// ... // ...
// }}, // }},
@ -124,6 +134,7 @@ var afterCallback = function(name){
// //
// //
// XXX handle .required options... // XXX handle .required options...
// XXX handle .default value
// XXX handle option types??? // XXX handle option types???
// XXX add support for outputting strings instead of console.log(..) // XXX add support for outputting strings instead of console.log(..)
// XXX --help should work for any command and not just for the nested // XXX --help should work for any command and not just for the nested
@ -195,10 +206,11 @@ object.Constructor('Parser', {
1 1
: ai - bi }) : ai - bi })
.map(function([e, _]){ return e }) }, .map(function([e, _]){ return e }) },
envOptions: function(){ optionsWithValue: function(){
return this.options() return this.options()
.filter(function([k, a, d, handler]){ .filter(function([k, a, d, handler]){
return !!handler.env }) }, return !!handler.env
|| 'default' in handler }) },
commands: function(){ commands: function(){
return this.options(this.commandPrefix) }, return this.options(this.commandPrefix) },
isCommand: function(str){ isCommand: function(str){
@ -237,6 +249,8 @@ object.Constructor('Parser', {
// doc stuff... // doc stuff...
helpColumnOffset: 3, helpColumnOffset: 3,
helpColumnPrefix: '- ', helpColumnPrefix: '- ',
//helpOptionSeparator: ' | ',
helpArgumentSeparator: ', ',
// doc sections... // doc sections...
version: undefined, version: undefined,
@ -266,13 +280,13 @@ object.Constructor('Parser', {
.replace(/\$SCRIPTNAME/g, this.scriptName) }, .replace(/\$SCRIPTNAME/g, this.scriptName) },
// Builtin options/commands... // Builtin options/commands...
// XXX might be a good idea to keep short options in a separate column...
'-h': '-help', '-h': '-help',
'-help': { '-help': {
doc: 'print this message and exit', doc: 'print this message and exit',
priority: 99, priority: 99,
handler: function(argv, key, value){ handler: function(argv, key, value){
var that = this var that = this
var sep = this.helpArgumentSeparator
var expandVars = this.expandTextVars.bind(this) var expandVars = this.expandTextVars.bind(this)
var formDoc = function(doc, env){ var formDoc = function(doc, env){
return [doc, ...(env ? return [doc, ...(env ?
@ -299,15 +313,22 @@ object.Constructor('Parser', {
// XXX add option groups... // XXX add option groups...
...section('Options', ...section('Options',
this.options() this.options()
.filter(function([o, a, doc]){
return doc !== false })
.map(function([opts, arg, doc, handler]){ .map(function([opts, arg, doc, handler]){
return [ return [
// XXX might be a good idea to keep
// short options in a separate
// column...
opts opts
.sort(function(a, b){ .sort(function(a, b){
return a.length - b.length}) return a.length - b.length})
.join(' | -') .map(function(o, i){
return o.length <= 2 ?
o
// no short options -> offset first long option...
: i == 0 ?
' '.repeat(sep.length + 2) +'-'+ o
// add extra '-' to long options...
: '-'+ o })
.join(sep)
+' '+ (arg || ''), +' '+ (arg || ''),
...formDoc(doc, handler.env) ] })), ...formDoc(doc, handler.env) ] })),
// dynamic options... // dynamic options...
@ -322,7 +343,7 @@ object.Constructor('Parser', {
return [ return [
cmd cmd
.map(function(cmd){ return cmd.slice(1)}) .map(function(cmd){ return cmd.slice(1)})
.join(' | ') .join(sep)
+' '+ (arg || ''), +' '+ (arg || ''),
...formDoc(doc, handler.env) ] })), ...formDoc(doc, handler.env) ] })),
// examples (optional)... // examples (optional)...
@ -354,19 +375,11 @@ object.Constructor('Parser', {
console.log(this.version || '0.0.0') console.log(this.version || '0.0.0')
return module.STOP }, }, return module.STOP }, },
/*/ XXX do we actually need this??? // XXX do we actually need this???
// doing this is trivial, need to give a practical example...
// XXX to stop processing we need to empty rest...
// ...need to workout a mechanic for this and then passing rest
// to .then(..)...
// ...one way to do this is returning a module.BREAK (rename???)...
// XXX would be nice to be able to take the rest of the options and
// pass them to .then(..)...
'-': { '-': {
doc: 'stop processing arguments', doc: 'stop processing arguments after this point',
handler: function(){ handler: function(){
return module.STOP }, }, return module.THEN }, },
//*/
// common short-hands... // common short-hands...
// //
@ -383,6 +396,7 @@ object.Constructor('Parser', {
// .handleArgument(args, arg) // .handleArgument(args, arg)
// -> module.ERROR // -> module.ERROR
// -> module.STOP // -> module.STOP
// -> module.THEN
// -> result // -> result
// //
// Get dynamic argument doc... // Get dynamic argument doc...
@ -438,6 +452,7 @@ object.Constructor('Parser', {
typeof(process) != 'unhandled' typeof(process) != 'unhandled'
&& process.exit(1) }, && process.exit(1) },
// post parsing callbacks... // post parsing callbacks...
// //
// .then(callback(unhandleed, root_value, rest)) // .then(callback(unhandleed, root_value, rest))
@ -449,6 +464,15 @@ object.Constructor('Parser', {
stop: afterCallback('stop'), stop: afterCallback('stop'),
error: afterCallback('error'), error: afterCallback('error'),
// remove callback...
off: function(evt, handler){
var l = this['__after_'+evt]
var i = l.indexOf(handler)
i >= 0
&& l.splice(i, 1)
return this },
// //
// parser(argv) // parser(argv)
// -> parser // -> parser
@ -494,14 +518,17 @@ object.Constructor('Parser', {
var runHandler = function(handler, arg, rest){ var runHandler = function(handler, arg, rest){
var [arg, value] = arg.split(/=/) var [arg, value] = arg.split(/=/)
// get option value... // get option value...
value = value value = value == null ?
|| ((handler.arg && !opt_pattern.test(rest[0])) ? ((handler.arg && !opt_pattern.test(rest[0])) ?
rest.shift() rest.shift()
: (typeof(process) != 'unhandled' && handler.env) ? : (typeof(process) != 'undefined' && handler.env) ?
process.env[handler.env] (process.env[handler.env]
: undefined) || handler.default)
: handler.default)
: value
// value conversion... // value conversion...
value = (value && that.handleArgumentValue) ? value = (value != null
&& that.handleArgumentValue) ?
that.handleArgumentValue(handler, value) that.handleArgumentValue(handler, value)
: value : value
// run handler... // run handler...
@ -511,7 +538,9 @@ object.Constructor('Parser', {
.call(that, .call(that,
rest, rest,
arg, arg,
...(value ? [value] : [])) ...(value != null ?
[value]
: []))
// handle .STOP / .ERROR // handle .STOP / .ERROR
if(res === module.STOP || res === module.ERROR){ if(res === module.STOP || res === module.ERROR){
that[res === module.STOP ? that[res === module.STOP ?
@ -542,7 +571,7 @@ object.Constructor('Parser', {
return handler return handler
&& [a, handler] } && [a, handler] }
var env = new Set() var values = new Set()
var unhandled = [] var unhandled = []
while(rest.length > 0){ while(rest.length > 0){
var arg = rest.shift() var arg = rest.shift()
@ -567,9 +596,10 @@ object.Constructor('Parser', {
;[arg, handler] = handler instanceof Array ? ;[arg, handler] = handler instanceof Array ?
handler handler
: [arg, handler] : [arg, handler]
// env handler called... // value handler called...
handler.env ;(handler.env
&& env.add(handler) || 'default' in handler)
&& values.add(handler)
var res = runHandler(handler, arg, rest) var res = runHandler(handler, arg, rest)
@ -578,17 +608,21 @@ object.Constructor('Parser', {
return nested ? return nested ?
res res
: this } : this }
// break processing -> .then(...)
if(res === module.THEN){
arg = null
break }
continue } continue }
// unhandled... // unhandled...
arg arg
&& unhandled.push(arg) } && unhandled.push(arg) }
// call env handlers that were not explicitly called yet... // call value handlers that were not explicitly called yet...
typeof(process) != 'unhandled' typeof(process) != 'unhandled'
&& this.envOptions() && this.optionsWithValue()
.forEach(function([k, a, d, handler]){ .forEach(function([k, a, d, handler]){
env.has(handler) values.has(handler)
|| (handler.env in process.env || ((handler.env in process.env || handler.default)
&& runHandler(handler, a, null, rest)) }) && runHandler(handler, a || k[0], null, rest)) })
// post handlers... // post handlers...
root_value = root_value && this.handleArgumentValue ? root_value = root_value && this.handleArgumentValue ?
this.handleArgumentValue(this, root_value) this.handleArgumentValue(this, root_value)

View File

@ -43,6 +43,7 @@ argv.Parser({
'-test': argv.Parser({ '-test': argv.Parser({
// XXX ENV // XXX ENV
env: 'TEST', env: 'TEST',
default: 'moo',
}).then(function(){ }).then(function(){
console.log('TEST', ...arguments) }), console.log('TEST', ...arguments) }),