1 | 'use strict'
|
---|
2 |
|
---|
3 | // hoisted due to circular dependency on command.
|
---|
4 | module.exports = argsert
|
---|
5 | const command = require('./command')()
|
---|
6 | const YError = require('./yerror')
|
---|
7 |
|
---|
8 | const positionName = ['first', 'second', 'third', 'fourth', 'fifth', 'sixth']
|
---|
9 | function argsert (expected, callerArguments, length) {
|
---|
10 | // TODO: should this eventually raise an exception.
|
---|
11 | try {
|
---|
12 | // preface the argument description with "cmd", so
|
---|
13 | // that we can run it through yargs' command parser.
|
---|
14 | let position = 0
|
---|
15 | let parsed = { demanded: [], optional: [] }
|
---|
16 | if (typeof expected === 'object') {
|
---|
17 | length = callerArguments
|
---|
18 | callerArguments = expected
|
---|
19 | } else {
|
---|
20 | parsed = command.parseCommand(`cmd ${expected}`)
|
---|
21 | }
|
---|
22 | const args = [].slice.call(callerArguments)
|
---|
23 |
|
---|
24 | while (args.length && args[args.length - 1] === undefined) args.pop()
|
---|
25 | length = length || args.length
|
---|
26 |
|
---|
27 | if (length < parsed.demanded.length) {
|
---|
28 | throw new YError(`Not enough arguments provided. Expected ${parsed.demanded.length} but received ${args.length}.`)
|
---|
29 | }
|
---|
30 |
|
---|
31 | const totalCommands = parsed.demanded.length + parsed.optional.length
|
---|
32 | if (length > totalCommands) {
|
---|
33 | throw new YError(`Too many arguments provided. Expected max ${totalCommands} but received ${length}.`)
|
---|
34 | }
|
---|
35 |
|
---|
36 | parsed.demanded.forEach((demanded) => {
|
---|
37 | const arg = args.shift()
|
---|
38 | const observedType = guessType(arg)
|
---|
39 | const matchingTypes = demanded.cmd.filter(type => type === observedType || type === '*')
|
---|
40 | if (matchingTypes.length === 0) argumentTypeError(observedType, demanded.cmd, position, false)
|
---|
41 | position += 1
|
---|
42 | })
|
---|
43 |
|
---|
44 | parsed.optional.forEach((optional) => {
|
---|
45 | if (args.length === 0) return
|
---|
46 | const arg = args.shift()
|
---|
47 | const observedType = guessType(arg)
|
---|
48 | const matchingTypes = optional.cmd.filter(type => type === observedType || type === '*')
|
---|
49 | if (matchingTypes.length === 0) argumentTypeError(observedType, optional.cmd, position, true)
|
---|
50 | position += 1
|
---|
51 | })
|
---|
52 | } catch (err) {
|
---|
53 | console.warn(err.stack)
|
---|
54 | }
|
---|
55 | }
|
---|
56 |
|
---|
57 | function guessType (arg) {
|
---|
58 | if (Array.isArray(arg)) {
|
---|
59 | return 'array'
|
---|
60 | } else if (arg === null) {
|
---|
61 | return 'null'
|
---|
62 | }
|
---|
63 | return typeof arg
|
---|
64 | }
|
---|
65 |
|
---|
66 | function argumentTypeError (observedType, allowedTypes, position, optional) {
|
---|
67 | throw new YError(`Invalid ${positionName[position] || 'manyith'} argument. Expected ${allowedTypes.join(' or ')} but received ${observedType}.`)
|
---|
68 | }
|
---|