reworked type handlers and value collectors + docs...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2020-07-27 19:02:43 +03:00
parent 3c00ffcaf8
commit d3762f4f9c
4 changed files with 104 additions and 66 deletions

View File

@ -398,8 +398,13 @@ occurrences of the option and write the result to `<key>`.
Supported collection modes: Supported collection modes:
- `"list"` &ndash; group values into an `Array` object - `"list"` &ndash; group values into an `Array` object
- `"set"` &ndash; group values into a `Set` object - `"set"` &ndash; group values into a `Set` object
- `"string"` &ndash; concatenate values into a string - `"string"` &ndash; concatenate values into a string.
- `"toggle"` &ndash; toggle option value (bool) This also supports an optional separator, for example `"string|\t"` will
collect values into a string joining them with a tab (i.e. `"\t"`).
Default separator is: `" "`
- `"toggle"` &ndash; toggle option value (bool).
Note that the actual value assigned to an option is ignored here and can
be omitted.
Type handlers are defined in `Parser.valueCollectors` or can be overwritten Type handlers are defined in `Parser.valueCollectors` or can be overwritten
by `<spec>.valueCollectors`. by `<spec>.valueCollectors`.
@ -407,10 +412,7 @@ by `<spec>.valueCollectors`.
`<option>.collect` can be used in conjunction with `<option>.type` to both `<option>.collect` can be used in conjunction with `<option>.type` to both
convert and collect values. convert and collect values.
If not set, each subsequent option repetition will overwrite the value. If not set, each subsequent option will overwrite the previously set value.
If `"toggle"` is set the actual value assigned to an option is ignored
and can be omitted.
#### `<option>.env` #### `<option>.env`

148
argv.js
View File

@ -128,6 +128,8 @@ function(name, pre, post){
// //
// type: 'int', // type: 'int',
// //
// collect: 'string|, ',
//
// env: 'VALUE', // env: 'VALUE',
// //
// default: 123, // default: 123,
@ -203,7 +205,6 @@ function(name, pre, post){
// currently both '-' and '+' are supported. // currently both '-' and '+' are supported.
// //
// 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 FEATURE (DELAYED): handle <scriptName>-<command> script calls... // XXX FEATURE (DELAYED): handle <scriptName>-<command> script calls...
@ -215,8 +216,11 @@ function(name, pre, post){
// //
// XXX should type handlers produce errors??? // XXX should type handlers produce errors???
// XXX add support for ParserError exception handling... // XXX add support for ParserError exception handling...
// XXX should -help should work for any command? ..not just nested parsers? // XXX should -help work for any command? ..not just nested parsers?
// ...should we indicate which thinks have more "-help"?? // ...should we indicate which thinks have more "-help"??
var Parser = var Parser =
module.Parser = module.Parser =
object.Constructor('Parser', { object.Constructor('Parser', {
@ -232,10 +236,15 @@ object.Constructor('Parser', {
.split(',') .split(',')
.map(function(e){ return e.trim() }) }, .map(function(e){ return e.trim() }) },
}, },
valueCollectors: { valueCollectors: {
string: function(v, cur){ return (cur || '') + v }, // format: 'string' | 'string|<separator>'
string: function(v, cur, sep){
return [...(cur ? [cur] : []), v]
.join(sep || '') },
list: function(v, cur){ return (cur || []).concat(v) }, list: function(v, cur){ return (cur || []).concat(v) },
set: function(v, cur){ return (cur || new Set()).add(v) }, set: function(v, cur){ return (cur || new Set()).add(v) },
// NOTE: this will ignore the actual value given...
toggle: function(v, cur){ return !cur }, toggle: function(v, cur){ return !cur },
}, },
@ -251,7 +260,6 @@ object.Constructor('Parser', {
optionInputPattern: /^([+-])\1?([^+-].*|)$/, optionInputPattern: /^([+-])\1?([^+-].*|)$/,
commandInputPattern: /^([^-].*)$/, commandInputPattern: /^([^-].*)$/,
// instance stuff... // instance stuff...
// XXX do we need all three??? // XXX do we need all three???
script: null, script: null,
@ -264,17 +272,7 @@ object.Constructor('Parser', {
value: null, value: null,
// output... // Handler iterators...
//
print: afterCallback('print', null, function(...args){
console.log(...args)
return this }),
printError: afterCallback('print_error', null, function(...args){
console.error(this.scriptName+': Error:', ...args)
return this }),
// Handler API...
// //
// Format: // Format:
// [ // [
@ -336,10 +334,14 @@ object.Constructor('Parser', {
return handler.required }) }, return handler.required }) },
commands: function(){ commands: function(){
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['@*']) }, || this['@*']) },
// Get handler...
//
// 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...
@ -368,6 +370,7 @@ object.Constructor('Parser', {
[] []
: ['dead-end'])] }, : ['dead-end'])] },
// Trigger the handler...
// //
// Get the handler for key and call it... // Get the handler for key and call it...
// .handle(key, rest, _, value) // .handle(key, rest, _, value)
@ -409,41 +412,8 @@ object.Constructor('Parser', {
this.setHandlerValue(handler, key, res) } this.setHandlerValue(handler, key, res) }
return res }, return res },
// set handler value...
//
// This handles handler.arg and basic name generation...
setHandlerValue: function(handler, key, value){
handler = handler
|| this.handler(key)[1]
|| {}
key = (handler.arg
&& handler.arg
.split(/\|/)
.pop()
.trim())
// get the final key...
|| this.handler(key)[0].slice(1)
// if value not given set true and handle...
//this[key] = arguments.length < 3 ?
value = arguments.length < 3 ?
(this.handleArgumentValue ?
this.handleArgumentValue(handler, true)
: true)
: value
var collect =
typeof(handler.collect) == 'function' ?
handler.collect
: (this.valueCollectors
|| this.constructor.valueCollectors
|| {})[handler.collect]
this[key] = collect ?
collect.call(this, value, this[key])
: value
return this },
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Builtin options/commands and their configuration... // Builtin options/commands and their configuration...
// Help... // Help...
@ -672,13 +642,81 @@ object.Constructor('Parser', {
'@*': '-*', '@*': '-*',
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Output...
//
print: afterCallback('print', null, function(...args){
console.log(...args)
return this }),
printError: afterCallback('print_error', null, function(...args){
console.error(this.scriptName+': Error:', ...args)
return this }),
// Handle value via this/parent value handlers... (helper)
//
// Expected attr format:
//
// option_handler[attr] = '<handler-name>' | '<handler-name>|<arg>|...'
//
//
// This will call the handler in this context with the following
// signature:
//
// handler(value, ...args, ...sargs)
//
// Where sargs is the list of arguments defined in attr via '|'.
//
// For an example see: .handleArgumentValue(..) and .setHandlerValue(..)
_handleValue: function(handler, attr, handlers, value, ...args){
var [h, ...sargs] =
typeof(handler[attr]) == typeof('str') ?
handler[attr].split(/\|/)
: []
var func =
typeof(handler[attr]) == 'function' ?
handler[attr]
: (this[handlers]
|| this.constructor[handlers]
|| {})[h]
return func ?
func.call(this, value, ...args, ...sargs)
: value },
// Set handler value... (helper)
//
// This handles handler.arg and basic name generation...
setHandlerValue: function(handler, key, value){
handler = handler
|| this.handler(key)[1]
|| {}
key = (handler.arg
&& handler.arg
.split(/\|/)
.pop()
.trim())
// get the final key...
|| this.handler(key)[0].slice(1)
// if value not given set true and handle...
//this[key] = arguments.length < 3 ?
value = arguments.length < 3 ?
(this.handleArgumentValue ?
this.handleArgumentValue(handler, true)
: true)
: value
this[key] = this._handleValue(handler, 'collect', 'valueCollectors', value, this[key])
return this },
// Default handler action... // Default handler action...
// //
// This is called when .handler is not set... // This is called when .handler is not set...
handlerDefault: function(handler, rest, key, value){ handlerDefault: function(handler, rest, key, value){
return this.setHandlerValue(handler, ...[...arguments].slice(2)) }, return this.setHandlerValue(handler, ...[...arguments].slice(2)) },
// 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...
@ -686,15 +724,7 @@ object.Constructor('Parser', {
// NOTE: to disable this functionality just set: // NOTE: to disable this functionality just set:
// handleArgumentValue: false // handleArgumentValue: false
handleArgumentValue: function(handler, value){ handleArgumentValue: function(handler, value){
var convert = return this._handleValue(handler, 'type', 'typeHandlers', value) },
typeof(handler.type) == 'function' ?
handler.type
: (this.typeHandlers
|| this.constructor.typeHandlers
|| {})[handler.type]
return convert ?
convert.call(this, value)
: value },
// Handle error exit... // Handle error exit...
// //

View File

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

View File

@ -69,6 +69,12 @@ argv.Parser({
arg: '| toggle', arg: '| toggle',
collect: 'toggle', collect: 'toggle',
}, },
'-s': '-string',
'-string': {
doc: 'collect STR',
arg: 'STR | str',
collect: 'string|\t',
},
'-test': argv.Parser({ '-test': argv.Parser({