0.0.7Updated 5 days ago
interface SchemaFieldBase {
  long: `--${string}`
  short?: `-${string}`
  default?: never
}

interface SchemaFlag extends SchemaFieldBase {
  type: 'flag'
}
interface SchemaString extends Omit<SchemaFieldBase, 'default'> {
  type: 'string'
  default?: string
}
interface SchemaNumber extends Omit<SchemaFieldBase, 'default'> {
  type: 'number'
  default?: number
}

type SchemaField = SchemaFlag | SchemaString | SchemaNumber

type SchemaFieldType<T extends SchemaField> =
  T['type'] extends 'flag' ? boolean :
  T['type'] extends 'string' ? string :
  T['type'] extends 'number' ? number :
  unknown;

type SchemaFieldTypeUndefinable<T extends SchemaField> =
  T['default'] extends string ? SchemaFieldType<T> :
  T['default'] extends number ? SchemaFieldType<T> :
  T['type'] extends 'flag' ? boolean :
  SchemaFieldType<T> | undefined;

const args = [...Deno.args];
const inputs: Record<string, string | boolean> = {}
while(args.length) {
  const arg = args.splice(0, 1)[0];
  if(arg.startsWith('-')) {
    if(args.length && !args[0].startsWith('-')) {
      const value = args.splice(0, 1)[0];
      inputs[arg] = value;
    } else {
      inputs[arg] = true;
    }
  }
}

export class Args {
  private constructor() {}

  static Parse<const T extends Record<string, SchemaField>>(schema: T) {
    const out: Record<string, unknown> = {}

    for(const [key, value] of Object.entries(schema)) {
      const tag_value = (value.short ? inputs[value.short] : undefined) || inputs[value.long];

      switch(value.type) {
        case "string": {
          out[key] = tag_value || value.default;
          break;
        }
        case "number": {
          if(typeof tag_value == 'string') {
            out[key] = Number(tag_value) || value.default;
          } else {
            out[key] = value.default;
          }
          break;
        }
        case "flag": {
          out[key] = !!tag_value;
        }
      }
    }

    return out as {[K in keyof T]: SchemaFieldTypeUndefinable<T[K]>}
  }
}