Add support for positional args in @rondo/argparse
This commit is contained in:
parent
7ea1c34428
commit
b50dfb1455
@ -125,6 +125,76 @@ describe('argparse', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('positional', () => {
|
||||||
|
it('can be defined', () => {
|
||||||
|
const parse = argparse({
|
||||||
|
a: {
|
||||||
|
type: 'number',
|
||||||
|
positional: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(parse([]).a).toBe(NaN)
|
||||||
|
expect(parse(['12']).a).toBe(12)
|
||||||
|
})
|
||||||
|
it('works with booleans', () => {
|
||||||
|
const parse = argparse({
|
||||||
|
a: {
|
||||||
|
type: 'boolean',
|
||||||
|
positional: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(parse([]).a).toBe(false)
|
||||||
|
expect(parse(['true']).a).toBe(true)
|
||||||
|
expect(parse(['false']).a).toBe(false)
|
||||||
|
expect(() => parse(['invalid'])).toThrowError(/true or false/)
|
||||||
|
})
|
||||||
|
it('works with strings', () => {
|
||||||
|
const parse = argparse({
|
||||||
|
a: {
|
||||||
|
type: 'string',
|
||||||
|
positional: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(parse([]).a).toBe('')
|
||||||
|
expect(parse(['a']).a).toBe('a')
|
||||||
|
})
|
||||||
|
it('works with multiple positionals', () => {
|
||||||
|
const parse = argparse({
|
||||||
|
a: {
|
||||||
|
type: 'string',
|
||||||
|
positional: true,
|
||||||
|
},
|
||||||
|
b: {
|
||||||
|
type: 'string',
|
||||||
|
positional: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(parse(['aaa', 'bbb'])).toEqual({
|
||||||
|
a: 'aaa',
|
||||||
|
b: 'bbb',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
it('works amongs regular arguments', () => {
|
||||||
|
const parse = argparse({
|
||||||
|
arg1: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
arg2: {
|
||||||
|
type: 'number',
|
||||||
|
positional: true,
|
||||||
|
},
|
||||||
|
arg3: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(parse(['--arg1', 'one', '2', '--arg3', 'three'])).toEqual({
|
||||||
|
arg1: 'one',
|
||||||
|
arg2: 2,
|
||||||
|
arg3: 'three',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
it('throws when required args missing', () => {
|
it('throws when required args missing', () => {
|
||||||
expect(() => argparse({
|
expect(() => argparse({
|
||||||
one: {
|
one: {
|
||||||
|
|||||||
@ -14,6 +14,7 @@ export interface IArgConfig<T extends TArgTypeName> {
|
|||||||
description?: string
|
description?: string
|
||||||
default?: TArgType<T>
|
default?: TArgType<T>
|
||||||
required?: boolean
|
required?: boolean
|
||||||
|
positional?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IArgsConfig {
|
export interface IArgsConfig {
|
||||||
@ -64,6 +65,7 @@ export const argparse = <T extends IArgsConfig>(
|
|||||||
const it = iterate(args)
|
const it = iterate(args)
|
||||||
|
|
||||||
const aliases: Record<string, string> = {}
|
const aliases: Record<string, string> = {}
|
||||||
|
const positional: string[] = []
|
||||||
const requiredArgs = Object.keys(config).reduce((obj, arg) => {
|
const requiredArgs = Object.keys(config).reduce((obj, arg) => {
|
||||||
const argConfig = config[arg]
|
const argConfig = config[arg]
|
||||||
result[arg] = argConfig.default !== undefined
|
result[arg] = argConfig.default !== undefined
|
||||||
@ -75,6 +77,9 @@ export const argparse = <T extends IArgsConfig>(
|
|||||||
'Duplicate alias: ' + argConfig.alias)
|
'Duplicate alias: ' + argConfig.alias)
|
||||||
aliases[argConfig.alias] = arg
|
aliases[argConfig.alias] = arg
|
||||||
}
|
}
|
||||||
|
if (argConfig.positional) {
|
||||||
|
positional.push(arg)
|
||||||
|
}
|
||||||
if (argConfig.required) {
|
if (argConfig.required) {
|
||||||
obj[arg] = true
|
obj[arg] = true
|
||||||
}
|
}
|
||||||
@ -108,25 +113,47 @@ export const argparse = <T extends IArgsConfig>(
|
|||||||
return lastArgName
|
return lastArgName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getNextPositional(): string {
|
||||||
|
const p = positional.shift()
|
||||||
|
assert(!!p, 'No defined positional arguments')
|
||||||
|
return p!
|
||||||
|
}
|
||||||
|
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
const arg = it.next()
|
const arg = it.next()
|
||||||
assert(arg.substring(0, 1) === '-', 'Arguments must start with -')
|
const isPositional = arg.substring(0, 1) !== '-'
|
||||||
const argName: string = processFlags(arg)
|
const argName = !isPositional
|
||||||
|
? processFlags(arg)
|
||||||
|
: getNextPositional()
|
||||||
const argConfig = config[argName]
|
const argConfig = config[argName]
|
||||||
assert(!!argConfig, 'Unknown argument: ' + arg)
|
assert(!!argConfig, 'Unknown argument: ' + arg)
|
||||||
delete requiredArgs[argName]
|
delete requiredArgs[argName]
|
||||||
const peek = it.peek()
|
const peek = it.peek()
|
||||||
switch (argConfig.type) {
|
switch (argConfig.type) {
|
||||||
case 'string':
|
case 'string':
|
||||||
assert(it.hasNext(), 'Value of argument must be a string: ' + arg)
|
if (isPositional) {
|
||||||
result[argName] = it.next()
|
result[argName] = arg
|
||||||
|
} else {
|
||||||
|
assert(it.hasNext(), 'Value of argument must be a string: ' + arg)
|
||||||
|
result[argName] = it.next()
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
case 'number':
|
case 'number':
|
||||||
const num = parseInt(it.next(), 10)
|
const num = parseInt(isPositional ? arg : it.next(), 10)
|
||||||
assert(!isNaN(num), 'Value of argument must be a number: ' + arg)
|
assert(!isNaN(num), 'Value of argument must be a number: ' + arg)
|
||||||
result[argName] = num
|
result[argName] = num
|
||||||
continue
|
continue
|
||||||
case 'boolean':
|
case 'boolean':
|
||||||
|
if (isPositional) {
|
||||||
|
if (arg === 'true') {
|
||||||
|
result[argName] = true
|
||||||
|
} else if (arg === 'false') {
|
||||||
|
result[argName] = false
|
||||||
|
} else {
|
||||||
|
assert(false, 'Value of argument must be true or false: ' + arg)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
if (peek === 'true') {
|
if (peek === 'true') {
|
||||||
it.next()
|
it.next()
|
||||||
result[argName] = true
|
result[argName] = true
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user