♻ Replace fail function by throwing error

This commit is contained in:
C-3PO 2018-10-25 06:13:24 +02:00
parent 76d9de1778
commit 640c8df6bc
Signed by: c3po
GPG key ID: 62993C4BB4D86F24
4 changed files with 34 additions and 34 deletions

View file

@ -11,26 +11,30 @@ import runPreParseHook from './runPreParseHook';
*/ */
export default function parseArguments( export default function parseArguments(
spec: { [key: string]: IOption }, spec: { [key: string]: IOption },
preParseHook?: (args: string[], fail: (message: string) => void) => (string[] | undefined), preParseHook?: (args: string[]) => (string[] | undefined),
): { fail: (errorMessage: string) => never, args: { [key: string]: string } } { ): { fail: (errorMessage: string) => never, args: { [key: string]: string } } {
//Initialize state //Initialize state
const { outputArgs, shortToLongLookup, requiredOptions, fail } = initState(spec); const { outputArgs, shortToLongLookup, requiredOptions, fail } = initState(spec);
const args = runPreParseHook({ args: process.argv.slice(2), preParseHook, fail }); try {
const args = runPreParseHook({ args: process.argv.slice(2), preParseHook });
//Iterate through all command line arguments. When we have read both name and value, verify the argument for correctness. //Iterate through all command line arguments. When we have read both name and value, verify the argument for correctness.
//Show error if a name has no value afterwards, or a value has no name in front of it. //Show error if a name has no value afterwards, or a value has no name in front of it.
const lastOption = args.reduce(parseArgument.bind(null, { spec, fail, outputArgs, requiredOptions, shortToLongLookup })); const lastOption = args.reduce(parseArgument.bind(null, { spec, outputArgs, requiredOptions, shortToLongLookup }));
// argumentName must be cleared to '' after the value is read, so if it is not an empty string, the value was missing // argumentName must be cleared to '' after the value is read, so if it is not an empty string, the value was missing
if (lastOption !== '') { if (lastOption !== '') {
return fail(`Option "${lastOption}" was not followed by a value.`); throw new Error(`Option "${lastOption}" was not followed by a value.`);
}
//check if any entry in requiredArguments was not set
for (const optName in requiredOptions) {
if (spec.hasOwnProperty(optName) && requiredOptions[optName] === false) {
return fail(`Missing option "${optName}" even though it is required.`);
} }
//check if any entry in requiredArguments was not set
for (const optName in requiredOptions) {
if (spec.hasOwnProperty(optName) && requiredOptions[optName] === false) {
throw new Error(`Missing option "${optName}" even though it is required.`);
}
}
} catch (error) {
fail((error as Error).message);
} }
return { return {

View file

@ -2,9 +2,8 @@ import IOption from './IOption';
import verifyAndStoreOptionValue from './verifyAndStoreOptionValue'; import verifyAndStoreOptionValue from './verifyAndStoreOptionValue';
export default function parseArgument( export default function parseArgument(
{ spec, fail, outputArgs, requiredOptions, shortToLongLookup }: { { spec, outputArgs, requiredOptions, shortToLongLookup }: {
spec: { [key: string]: IOption }, spec: { [key: string]: IOption },
fail: (errorMessage: string) => never,
outputArgs: { [key: string]: string }, outputArgs: { [key: string]: string },
requiredOptions: { [key: string]: boolean }, requiredOptions: { [key: string]: boolean },
shortToLongLookup: { [key: string]: string }, shortToLongLookup: { [key: string]: string },
@ -20,7 +19,7 @@ export default function parseArgument(
* - `-o=value` * - `-o=value`
*/ */
if (arg === '-' || arg === '--') { if (arg === '-' || arg === '--') {
return fail(`Empty option "-" or "--" is not supported.`); throw new Error(`Empty option "-" or "--" is not supported.`);
} else if (arg.startsWith('--')) { //long argument } else if (arg.startsWith('--')) { //long argument
let option = arg.substr(2); let option = arg.substr(2);
let value; let value;
@ -30,11 +29,11 @@ export default function parseArgument(
} }
//Check that argument name is valid //Check that argument name is valid
if (spec[option] === undefined) { if (spec[option] === undefined) {
return fail(`Unknown option "--${option}".`); throw new Error(`Unknown option "--${option}".`);
} }
//If value was provided, check that value is correct and remove name for next loop iteration //If value was provided, check that value is correct and remove name for next loop iteration
if (value !== undefined) { if (value !== undefined) {
verifyAndStoreOptionValue({option, value, verify: spec[option].verify, message: spec[option].message, fail, outputArgs, requiredOptions}); verifyAndStoreOptionValue({option, value, verify: spec[option].verify, message: spec[option].message, outputArgs, requiredOptions});
return ''; return '';
} }
return option; return option;
@ -47,20 +46,20 @@ export default function parseArgument(
} }
//Check that argument name is valid //Check that argument name is valid
if (shortToLongLookup[option] === undefined) { if (shortToLongLookup[option] === undefined) {
return fail(`Unknown short option "-${option}".`); throw new Error(`Unknown short option "-${option}".`);
} }
option = shortToLongLookup[option]; option = shortToLongLookup[option];
//If value was provided, check that value is correct and remove name for next loop iteration //If value was provided, check that value is correct and remove name for next loop iteration
if (value !== undefined) { if (value !== undefined) {
verifyAndStoreOptionValue({option, value, verify: spec[option].verify, message: spec[option].message, fail, outputArgs, requiredOptions}); verifyAndStoreOptionValue({option, value, verify: spec[option].verify, message: spec[option].message, outputArgs, requiredOptions});
return ''; return '';
} }
return option; return option;
} else { } else {
return fail(`Arguments must be preceded by an option but there was no option in front of "${arg}".`); throw new Error(`Arguments must be preceded by an option but there was no option in front of "${arg}".`);
} }
} else { //We expect a value, can be anything } else { //We expect a value, can be anything
verifyAndStoreOptionValue({option: previousOption, value: arg, verify: spec[previousOption].verify, message: spec[previousOption].message, fail, outputArgs, requiredOptions}); verifyAndStoreOptionValue({option: previousOption, value: arg, verify: spec[previousOption].verify, message: spec[previousOption].message, outputArgs, requiredOptions});
return ''; return '';
} }
} }

View file

@ -1,17 +1,16 @@
/** Runs the given pre-parse hook, if defined, to modify the command line arguments. Returns the modified command line arguments. */ /** Runs the given pre-parse hook, if defined, to modify the command line arguments. Returns the modified command line arguments. */
export default function runPreParseHook({ args, preParseHook, fail }: { export default function runPreParseHook({ args, preParseHook }: {
args: string[], args: string[],
preParseHook?: (args: string[], fail: (message: string) => void) => (string[] | undefined), preParseHook?: (args: string[]) => (string[] | undefined),
fail: (errorMessage: string) => never,
}): string[] { }): string[] {
if (preParseHook === undefined) { if (preParseHook === undefined) {
return args; return args;
} }
try { try {
const modifiedArgs = preParseHook(args, fail); const modifiedArgs = preParseHook(args);
return modifiedArgs !== undefined ? modifiedArgs : args; return modifiedArgs !== undefined ? modifiedArgs : args;
} catch (error) { } catch (error) {
return fail(`Could not run pre-parse hook, encountered error: ${String(error)}.`); throw new Error(`Could not run pre-parse hook, encountered error: ${String(error)}.`);
} }
} }

View file

@ -5,7 +5,6 @@ export default function verifyAndStoreOptionValue({
value, value,
verify, verify,
message, message,
fail,
outputArgs, outputArgs,
requiredOptions, requiredOptions,
}: { }: {
@ -13,7 +12,6 @@ export default function verifyAndStoreOptionValue({
value: string, value: string,
verify: IOption['verify'], verify: IOption['verify'],
message: IOption['message'], message: IOption['message'],
fail: (errorMessage: string) => never,
outputArgs: { [key: string]: string }, outputArgs: { [key: string]: string },
requiredOptions: { [key: string]: boolean }, requiredOptions: { [key: string]: boolean },
}) { }) {
@ -24,16 +22,16 @@ export default function verifyAndStoreOptionValue({
try { try {
const customMessage = message(value); const customMessage = message(value);
if (customMessage !== undefined && customMessage !== '') { if (customMessage !== undefined && customMessage !== '') {
return fail(customMessage); throw new Error(customMessage);
} }
} catch (error) { } catch (error) {
return fail(`Invalid value set for option "${option}": "${value}". Could not show custom error message: ${String(error)}`); throw new Error(`Invalid value set for option "${option}": "${value}". Could not show custom error message: ${String(error)}`);
} }
} }
return fail(`Invalid value set for option "${option}": "${value}".`); throw new Error(`Invalid value set for option "${option}": "${value}".`);
} }
} catch (error) { } catch (error) {
return fail(`Invalid value set for option "${option}", the validation of "${value}" failed: ${String(error)}`); throw new Error(`Invalid value set for option "${option}", the validation of "${value}" failed: ${String(error)}`);
} }
//Remember the value, and if it is a required argument, mark that we have encountered it //Remember the value, and if it is a required argument, mark that we have encountered it