reworked part of dynamic handling...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2020-07-06 06:07:02 +03:00
parent 5d45c9827f
commit 29a6c44923
2 changed files with 55 additions and 46 deletions

99
argv.js
View File

@ -157,8 +157,6 @@ var afterCallback = function(name){
// NOTE: essentially this parser is a very basic stack language... // NOTE: essentially this parser is a very basic stack language...
// XXX can we implement the whole thing directly as a stack language??? // XXX can we implement the whole thing directly as a stack language???
// //
// XXX should the undefined flags/comands be handled by '-*' and '@*' handlers?
// ...we would need a way to quote '*' to use it as an arg explicitly...
// XXX add -about flag??? // XXX add -about flag???
// XXX we should be able to set .scriptName by hand... // XXX we should be able to set .scriptName by hand...
// XXX might be a good idea to read metadata from package.json // XXX might be a good idea to read metadata from package.json
@ -194,7 +192,8 @@ object.Constructor('Parser', {
// output... // output...
// //
// XXX is this the right way to go??? // XXX how do we return something from these???
// ...closure?? ...globals?
print: function(...args){ print: function(...args){
console.log(...args) console.log(...args)
return this }, return this },
@ -238,7 +237,9 @@ object.Constructor('Parser', {
.split(/\|/) .split(/\|/)
.shift() .shift()
.trim(), .trim(),
h.doc || k.slice(1), h.doc == null ?
k.slice(1)
: h.doc,
h ])) }) h ])) })
return Object.values(handlers) }) return Object.values(handlers) })
.flat(1) .flat(1)
@ -268,7 +269,8 @@ object.Constructor('Parser', {
return this.options(this.commandPrefix) }, return this.options(this.commandPrefix) },
isCommand: function(str){ isCommand: function(str){
return this.commandInputPattern.test(str) return this.commandInputPattern.test(str)
&& (this.commandPrefix + str) in this }, && ((this.commandPrefix + str) in this
|| this['@*']) },
// NOTE: this ignores any arguments values present in the key... // NOTE: this ignores any arguments values present in the key...
// NOTE: this ignores options forming alias loops and dead-end // NOTE: this ignores options forming alias loops and dead-end
// options... // options...
@ -288,7 +290,6 @@ object.Constructor('Parser', {
return [key, undefined, return [key, undefined,
// report loop... // report loop...
'loop', [...seen, key]] } 'loop', [...seen, key]] }
//throw new Error('Option loop detected: '+ ([...seen, key].join(' -> '))) }
seen.add(key) } seen.add(key) }
return [key, this[key], return [key, this[key],
// report dead-end if this[key] is undefined... // report dead-end if this[key] is undefined...
@ -363,11 +364,17 @@ object.Constructor('Parser', {
...(info.length > 0 ? ...(info.length > 0 ?
['('+ info +')'] ['('+ info +')']
: [])] } : [])] }
var getValue = function(name){ var getValue = function(src, name){
return that[name] ? name = arguments.length == 1 ?
['', typeof(that[name]) == 'function' ? src
that[name]() : name
: that[name]] src = arguments.length == 1 ?
that
: src
return src[name] ?
['', typeof(src[name]) == 'function' ?
src[name]()
: src[name]]
: [] } : [] }
var section = function(title, items){ var section = function(title, items){
items = items instanceof Array ? items : [items] items = items instanceof Array ? items : [items]
@ -387,6 +394,8 @@ object.Constructor('Parser', {
.filter(function([o, a, doc]){ .filter(function([o, a, doc]){
return doc !== false }) return doc !== false })
.map(function([opts, arg, doc, handler]){ .map(function([opts, arg, doc, handler]){
opts = handler.key || opts
opts = opts instanceof Array ? opts : [opts]
return [ return [
[opts [opts
.sort(function(a, b){ .sort(function(a, b){
@ -407,12 +416,14 @@ object.Constructor('Parser', {
...formDoc(doc, handler) ] })), ...formDoc(doc, handler) ] })),
// dynamic options... // dynamic options...
...section('Dynamic options', ...section('Dynamic options',
this.handleArgument ? (this['-*'] && this['-*'].section_doc) ?
this.handleArgument('doc') || [] getValue(this['-*'], 'section_doc') || []
: []), : []),
// commands (optional)... // commands (optional)...
...section('Commands', ...section('Commands',
this.commands() this.commands()
.filter(function([o, a, doc]){
return doc !== false })
.map(function([cmd, arg, doc, handler]){ .map(function([cmd, arg, doc, handler]){
return [ return [
[cmd [cmd
@ -423,6 +434,11 @@ object.Constructor('Parser', {
: [])] : [])]
.join(that.helpValueSeparator), .join(that.helpValueSeparator),
...formDoc(doc, handler) ] })), ...formDoc(doc, handler) ] })),
// dynamic commands...
...section('Dynamic commands',
(this['@*'] && this['@*'].section_doc) ?
getValue(this['@*'], 'section_doc') || []
: []),
// examples (optional)... // examples (optional)...
...section('Examples', ...section('Examples',
this.examples instanceof Array ? this.examples instanceof Array ?
@ -470,6 +486,27 @@ object.Constructor('Parser', {
doc: 'stop processing arguments after this point', doc: 'stop processing arguments after this point',
handler: function(){ handler: function(){
return module.THEN }, }, return module.THEN }, },
// Dynamic handlers...
//
// These can be presented in help in two sections:
// Options / Commands
// .doc is a string
// .key can be used to override the option text
//
// Dynamic options / Dynamic commands
// .section_doc is a string or array
//
// XXX need a way to quote '*' to make it usable in flags/commands...
'-*': {
//key: '-*',
doc: false,
//section_doc: ...,
handler: function(_, key){
this.printError('Unknown '+ (key.startsWith('-') ? 'option:' : 'command:'), key)
return module.ERROR } },
'@*': '-*',
// Default handler action... // Default handler action...
@ -493,37 +530,6 @@ object.Constructor('Parser', {
return this }, return this },
// Handle arguments with no explicit handlers found...
//
// Handle dynamic/unknown argument...
// .handleArgument(args, arg)
// -> module.ERROR
// -> module.STOP
// -> module.THEN
// -> result
//
// Get dynamic argument doc...
// .handleArgument('doc')
// -> undefined
// -> doc
//
//
// doc format:
// [
// [<option-spec>, <doc>],
// ...
// ]
//
//
// NOTE: this is mainly needed to handle dynamic arguments or print
// error on unknown options (default)...
handleArgument: function(_, key){
// doc handler...
if(arguments.length == 1 && arguments[0] == 'doc'){
return undefined }
this.printError('Unknown '+ (key.startsWith('-') ? 'option:' : 'command:'), key)
return module.ERROR },
// Handle argument value conversion... // Handle argument value conversion...
// //
// If this is false/undefined value is passed to the handler as-is... // If this is false/undefined value is passed to the handler as-is...
@ -707,7 +713,10 @@ object.Constructor('Parser', {
&& parsed.splitOptions && parsed.splitOptions
&& splitArgs(arg, rest)) && splitArgs(arg, rest))
// dynamic or error... // dynamic or error...
|| parsed.handleArgument || parsed.handler(
type == 'opt' ?
'-*'
: '@*')[1]
// normalize output of splitArgs(..) // normalize output of splitArgs(..)
;[arg, handler] = handler instanceof Array ? ;[arg, handler] = handler instanceof Array ?
handler handler

View File

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