Files
issue-scheduler/src/cli.ts

122 lines
3.9 KiB
TypeScript

export interface CLIConfig {
// path to config file
config_filepath: string,
git_client: "gitea" | "github",
log_file: string | undefined,
// verbose logging
verbose: boolean,
// doesn't log to stdout/stderr (still logs to file if --log-file)
quiet: boolean
}
export const defaults: CLIConfig = {
config_filepath: "config.toml",
git_client: "gitea",
log_file: undefined,
verbose: false,
quiet: false
}
export class EmptyArgumentError extends Error {}
export class NoValueExpected extends Error {}
export class InvalidValue extends Error {}
/**
* Searches for first argument from flags options
* Valid values don't start with dashes
* @example Flag + Valid value
* getFlagValue(["--config", "config.toml"], ["--config", "-c"])
* // result: "config.toml"
* @example Flag + No value
* getFlagValue(["-c", "--verbose"], ["--config", "-c"])
* // result: null
* @example No Flag
* getFlagValue(["config.toml"], ["--config", "-c"])
* // result: undefined
*
* @param args An array of all command-line arguments
* @param flags An array of flag strings to search for
* @return {string}: When a flag is found and has a valid value
* @return {null}: When a flag is found but has no valid value
* @return {undefined}: When no flag is found in the arguments
*
*/
export function getFlagValue(args: string[], flags: string[]): string | null | undefined {
const index = args.findIndex(arg => flags.includes(arg))
if (index == -1) return undefined
if (index == args.length - 1) return null
const next = args[index + 1]!
if (next.startsWith("-")) {
return null
}
return next
}
/**
* Used for when the flag is optional, but a value is required if the flag appears.
* @throws EmptyArgumentError when the flag is present but has no value.
* @returns string | undefined — string when present with value, undefined when flag not present
*/
export function optionalStringFlag(args: string[], flags: string[]): string | undefined {
const value = getFlagValue(args, flags)
if (value === null) {
const errorMessage = `${flags.join(", ")} flag requires a value`
throw new EmptyArgumentError(errorMessage)
}
return value
}
export function optionalStringLiteralFlag<Option extends string>(
args: string[], flags: string[], options: Option[]
): Option | undefined {
const value = getFlagValue(args, flags)
if (value === null) {
const errorMessage = `${flags.join(", ")} flag requires a value`
throw new EmptyArgumentError(errorMessage)
}
if (value && !(options as string[]).includes(value)) {
const errorMessage = `${flags.join(", ")} flag requires one of these options ${options.join(", ")}`
throw new InvalidValue(errorMessage)
}
return value as (Option | undefined)
}
/**
* ...
*/
export function optionalBooleanFlag(args: string[], flags: string[]): boolean {
const value = getFlagValue(args, flags)
if (typeof value === "string") {
throw new NoValueExpected()
}
return value === null;
}
/**
* Parse command line arguments from a string array (process.argv)
* @param args Command line arguments
* @return {CLIConfig} Parsed flags
*/
export function parseArgs(args: string[]): CLIConfig {
const config = defaults
const configValue = optionalStringFlag(args, ["--config", "-c"])
if (configValue) config.config_filepath = configValue
const gitClientValue = optionalStringLiteralFlag(args, ["--git-client", "-gc"], ["gitea", "github"])
if (gitClientValue) config.git_client = gitClientValue
const logFileValue = optionalStringFlag(args, ["--log-file", "-lf"])
if (logFileValue) config.log_file = logFileValue
const verboseValue = optionalBooleanFlag(args, ["--verbose"])
if (verboseValue) config.verbose = verboseValue
const quietValue = optionalBooleanFlag(args, ["--quiet"])
if (quietValue) config.quiet = quietValue
return config
}