import { ParamDefinition, ParamDefinitionAny, ParamDefinitions, ParamDefinitionsMap, TasksMap } from "../../types"; import { PolarError } from "../core/errors"; import { ERRORS } from "../core/errors-list"; import { cmpStr } from "../util/strings"; import { ArgumentsParser } from "./arguments-parser"; const getMax = (a: number, b: number): number => Math.max(a, b); export class HelpPrinter { constructor ( private readonly _programName: string, private readonly _version: string, private readonly _paramDefs: ParamDefinitions, private readonly _tasks: TasksMap ) {} public printGlobalHelp (includeInternalTasks = false): void { console.log(`${this._programName} version ${this._version}\n`); console.log( `Usage: ${this._programName} [GLOBAL OPTIONS] [TASK OPTIONS]\n` ); console.log("GLOBAL OPTIONS:\n"); this._printParamDetails(this._paramDefs); console.log("\n\nAVAILABLE TASKS:\n"); const tasksToShow: TasksMap = {}; for (const [taskName, taskDefinition] of Object.entries(this._tasks)) { if (includeInternalTasks || !taskDefinition.isInternal) { tasksToShow[taskName] = taskDefinition; } } const nameLength = Object.keys(tasksToShow) .map((n) => n.length) .reduce((a, b) => Math.max(a, b), 0); for (const name of Object.keys(tasksToShow).sort(cmpStr)) { const { description = "" } = this._tasks[name]; console.log(` ${name.padEnd(nameLength)}\t${description}`); } console.log(""); console.log( `To get help for a specific task run: ${this._programName} help [task]\n` ); } public printTaskHelp (taskName: string): void { const taskDefinition = this._tasks[taskName]; if (taskDefinition === undefined) { throw new PolarError(ERRORS.ARGUMENTS.UNRECOGNIZED_TASK, { task: taskName }); } const { description = "", name, paramDefinitions, positionalParamDefinitions } = taskDefinition; console.log(`${this._programName} version ${this._version}\n`); const paramsList = this._getParamsList(paramDefinitions); const positionalParamsList = this._getPositionalParamsList( positionalParamDefinitions ); console.log( `Usage: ${this._programName} [GLOBAL OPTIONS] ${name}${paramsList}${positionalParamsList}\n` ); if (Object.keys(paramDefinitions).length > 0) { console.log("OPTIONS:\n"); this._printParamDetails(paramDefinitions); console.log(""); } if (positionalParamDefinitions.length > 0) { console.log("POSITIONAL ARGUMENTS:\n"); this._printPositionalParamDetails(positionalParamDefinitions); console.log(""); } console.log(`${name}: ${description}\n`); console.log(`For global options help run: ${this._programName} help\n`); } private _getParamValueDescription(paramDefinition: ParamDefinition): string { return `<${paramDefinition.type.name.toUpperCase()}>`; } private _getParamsList (paramDefinitions: ParamDefinitionsMap): string { let paramsList = ""; for (const name of Object.keys(paramDefinitions).sort(cmpStr)) { const definition = paramDefinitions[name]; const { defaultValue, isFlag } = definition; paramsList += " "; if (defaultValue !== undefined) { paramsList += "["; } paramsList += `${ArgumentsParser.paramNameToCLA(name)}`; if (!isFlag) { paramsList += ` ${this._getParamValueDescription(definition)}`; } if (defaultValue !== undefined) { paramsList += "]"; } } return paramsList; } private _getPositionalParamsList ( positionalParamDefinitions: ParamDefinitionAny[] ): string { let paramsList = ""; for (const definition of positionalParamDefinitions) { const { defaultValue, isVariadic, name } = definition; paramsList += " "; if (defaultValue !== undefined) { paramsList += "["; } if (isVariadic) { paramsList += "..."; } paramsList += name; if (defaultValue !== undefined) { paramsList += "]"; } } return paramsList; } private _printParamDetails (paramDefinitions: ParamDefinitionsMap): void { const paramKeys = Object.keys(paramDefinitions); const shortParamsNameLength = paramKeys .map((n) => ArgumentsParser.shortParamNameToCLA(paramDefinitions[n].shortName).length) .reduce(getMax, 0); const paramsNameLength = paramKeys .map((n) => ArgumentsParser.paramNameToCLA(n).length) .reduce(getMax, 0); for (const name of paramKeys.sort(cmpStr)) { const { description, defaultValue, isOptional, isFlag, shortName } = paramDefinitions[name]; const paddedShortName = ArgumentsParser.shortParamNameToCLA(shortName) .padEnd(shortParamsNameLength) + (shortName ? "," : " "); let msg = ` ${paddedShortName} `; msg += `${ArgumentsParser.paramNameToCLA(name).padEnd( paramsNameLength )}\t`; if (description !== undefined) { msg += `${description} `; } if (isOptional && defaultValue !== undefined && !isFlag) { msg += `(default: ${JSON.stringify(defaultValue)})`; } console.log(msg); } } private _printPositionalParamDetails ( positionalParamDefinitions: ParamDefinitionAny[] ): void { const paramsNameLength = positionalParamDefinitions .map((d) => d.name.length) .reduce((a, b) => Math.max(a, b), 0); for (const definition of positionalParamDefinitions) { const { name, description, isOptional, defaultValue } = definition; let msg = ` ${name.padEnd(paramsNameLength)}\t`; if (description !== undefined) { msg += `${description} `; } if (isOptional && defaultValue !== undefined) { msg += `(default: ${JSON.stringify(defaultValue)})`; } console.log(msg); } } }