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:
- `"list"` &ndash; group values into an `Array` object
- `"set"` &ndash; group values into a `Set` object
- `"string"` &ndash; concatenate values into a string
- `"toggle"` &ndash; toggle option value (bool)
- `"string"` &ndash; concatenate values into a string.
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
by `<spec>.valueCollectors`.
@ -407,10 +412,7 @@ by `<spec>.valueCollectors`.
`<option>.collect` can be used in conjunction with `<option>.type` to both
convert and collect values.
If not set, each subsequent option repetition will overwrite the value.
If `"toggle"` is set the actual value assigned to an option is ignored
and can be omitted.
If not set, each subsequent option will overwrite the previously set value.
#### `<option>.env`

148
argv.js
View File

@ -128,6 +128,8 @@ function(name, pre, post){
//
// type: 'int',
//
// collect: 'string|, ',
//
// env: 'VALUE',
//
// default: 123,
@ -203,7 +205,6 @@ function(name, pre, post){
// currently both '-' and '+' are supported.
//
// 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...
@ -215,8 +216,11 @@ function(name, pre, post){
//
// XXX should type handlers produce errors???
// 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"??
var Parser =
module.Parser =
object.Constructor('Parser', {
@ -232,10 +236,15 @@ object.Constructor('Parser', {
.split(',')
.map(function(e){ return e.trim() }) },
},
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) },
set: function(v, cur){ return (cur || new Set()).add(v) },
// NOTE: this will ignore the actual value given...
toggle: function(v, cur){ return !cur },
},
@ -251,7 +260,6 @@ object.Constructor('Parser', {
optionInputPattern: /^([+-])\1?([^+-].*|)$/,
commandInputPattern: /^([^-].*)$/,
// instance stuff...
// XXX do we need all three???
script: null,
@ -264,17 +272,7 @@ object.Constructor('Parser', {
value: null,
// 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 }),
// Handler API...
// Handler iterators...
//
// Format:
// [
@ -336,10 +334,14 @@ object.Constructor('Parser', {
return handler.required }) },
commands: function(){
return this.options(this.commandPrefix) },
isCommand: function(str){
return this.commandInputPattern.test(str)
&& ((this.commandPrefix + str) in this
|| this['@*']) },
// Get handler...
//
// NOTE: this ignores any arguments values present in the key...
// NOTE: this ignores options forming alias loops and dead-end
// options...
@ -368,6 +370,7 @@ object.Constructor('Parser', {
[]
: ['dead-end'])] },
// Trigger the handler...
//
// Get the handler for key and call it...
// .handle(key, rest, _, value)
@ -409,41 +412,8 @@ object.Constructor('Parser', {
this.setHandlerValue(handler, key, 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...
// 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...
//
// This is called when .handler is not set...
handlerDefault: function(handler, rest, key, value){
return this.setHandlerValue(handler, ...[...arguments].slice(2)) },
// Handle argument value conversion...
//
// 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:
// handleArgumentValue: false
handleArgumentValue: function(handler, value){
var convert =
typeof(handler.type) == 'function' ?
handler.type
: (this.typeHandlers
|| this.constructor.typeHandlers
|| {})[handler.type]
return convert ?
convert.call(this, value)
: value },
return this._handleValue(handler, 'type', 'typeHandlers', value) },
// Handle error exit...
//

View File

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

View File

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