/
argsert.ts
101 lines (93 loc) · 2.84 KB
/
argsert.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import {YError} from './yerror.js';
import {parseCommand, ParsedCommand} from './parse-command.js';
const positionName = ['first', 'second', 'third', 'fourth', 'fifth', 'sixth'];
export function argsert(callerArguments: any[], length?: number): void;
export function argsert(
expected: string,
callerArguments: any[],
length?: number
): void;
export function argsert(
arg1: string | any[],
arg2?: any[] | number,
arg3?: number
): void {
function parseArgs(): [
Pick<ParsedCommand, 'demanded' | 'optional'>,
any[],
number?
] {
return typeof arg1 === 'object'
? [{demanded: [], optional: []}, arg1, arg2 as number | undefined]
: [
parseCommand(`cmd ${arg1}`),
arg2 as any[],
arg3 as number | undefined,
];
}
// TODO: should this eventually raise an exception.
try {
// preface the argument description with "cmd", so
// that we can run it through yargs' command parser.
let position = 0;
const [parsed, callerArguments, _length] = parseArgs();
const args = [].slice.call(callerArguments);
while (args.length && args[args.length - 1] === undefined) args.pop();
const length = _length || args.length;
if (length < parsed.demanded.length) {
throw new YError(
`Not enough arguments provided. Expected ${parsed.demanded.length} but received ${args.length}.`
);
}
const totalCommands = parsed.demanded.length + parsed.optional.length;
if (length > totalCommands) {
throw new YError(
`Too many arguments provided. Expected max ${totalCommands} but received ${length}.`
);
}
parsed.demanded.forEach(demanded => {
const arg = args.shift();
const observedType = guessType(arg);
const matchingTypes = demanded.cmd.filter(
type => type === observedType || type === '*'
);
if (matchingTypes.length === 0)
argumentTypeError(observedType, demanded.cmd, position);
position += 1;
});
parsed.optional.forEach(optional => {
if (args.length === 0) return;
const arg = args.shift();
const observedType = guessType(arg);
const matchingTypes = optional.cmd.filter(
type => type === observedType || type === '*'
);
if (matchingTypes.length === 0)
argumentTypeError(observedType, optional.cmd, position);
position += 1;
});
} catch (err) {
console.warn((err as Error).stack);
}
}
function guessType(arg: any) {
if (Array.isArray(arg)) {
return 'array';
} else if (arg === null) {
return 'null';
}
return typeof arg;
}
function argumentTypeError(
observedType: string,
allowedTypes: string[],
position: number
) {
throw new YError(
`Invalid ${
positionName[position] || 'manyith'
} argument. Expected ${allowedTypes.join(
' or '
)} but received ${observedType}.`
);
}