<commit_before>import {promises as fs} from 'fs';
import * as path from 'path';

import {root} from './Fig';
import {COLORS, LOG_LEVEL, log, setLogLevel} from './console';
import dedent from './dedent';
import ErrorWithMetadata from './ErrorWithMetadata';
import escapeRegExpPattern from './escapeRegExpPattern';
import readAspect from './readAspect';

import type {LogLevel} from './console';

type Options = {
  logLevel: LogLevel;
  startAt: {
    found: boolean;
    literal: string;
    fuzzy?: RegExp;
  };
  testsOnly: boolean;
};

const {bold} = COLORS;

export default async function getOptions(
  args: Array<string>
): Promise<Options> {
  const options: Options = {
    logLevel: LOG_LEVEL.INFO,
    startAt: {
      found: false,
      literal: '',
    },
    testsOnly: false,
  };

  for (const arg of args) {
    if (arg === '--debug' || arg === '-d') {
      options.logLevel = LOG_LEVEL.DEBUG;
    } else if (arg === '--quiet' || arg === '-q') {
      options.logLevel = LOG_LEVEL.NOTICE;
    } else if (arg === '--test' || arg === '-t') {
      options.testsOnly = true;
    } else if (arg === '--help' || arg === '-h') {
      await printUsage();
      throw new ErrorWithMetadata('aborting');
    } else if (arg.startsWith('--start-at-task=')) {
      options.startAt.literal = (
        arg.match(/^--start-at-task=(.*)/)?.[1] ?? ''
      ).trim();
      options.startAt.fuzzy = new RegExp(
        [
          '',
          ...options.startAt.literal.split(/\s+/).map(escapeRegExpPattern),
          '',
        ].join('.*'),
        'i'
      );
    } else if (arg.startsWith('-')) {
      throw new ErrorWithMetadata(`unrecognized argument ${arg}`);
    } else {
      // TODO: error for bad aspects
    }
  }

  return options;
}

async function printUsage() {
  const directory = path.join(root, 'aspects');

  const entries = await fs.readdir(directory, {withFileTypes: true});

  const aspects = entries
    .filter((entry) => entry.isDirectory())
    .map((entry) => entry.name);

  // TODO: actually implement all the switches mentioned here
  log(
    dedent`

      ./install [options] [aspects...]

      ${bold`Options:`}

        -c/--check # not yet implemented
        -d/--debug
        -f/--force # not yet implemented
        -h/--help
        -q/--quiet
        -t/--test
        -v/--verbose (repeat up to four times for more verbosity) # not yet implemented
        --start-at-task='aspect | task' # TODO: maybe make -s short variant
        --step # not yet implemented

      ${bold`Aspects:`}
    `
  );

  for (const aspect of aspects) {
    const {description} = await readAspect(
      path.join(directory, aspect, 'aspect.json')
    );

    log(`  ${aspect}`);
    log(`    ${description}`);
  }<commit_msg>refactor: pass aspect info into printUsage()<commit_after>import {promises as fs} from 'fs';
import * as path from 'path';

import {root} from './Fig';
import {COLORS, LOG_LEVEL, log, setLogLevel} from './console';
import dedent from './dedent';
import ErrorWithMetadata from './ErrorWithMetadata';
import escapeRegExpPattern from './escapeRegExpPattern';
import readAspect from './readAspect';

import type {LogLevel} from './console';

type Options = {
  logLevel: LogLevel;
  startAt: {
    found: boolean;
    literal: string;
    fuzzy?: RegExp;
  };
  testsOnly: boolean;
};

const {bold} = COLORS;

export default async function getOptions(
  args: Array<string>
): Promise<Options> {
  const options: Options = {
    logLevel: LOG_LEVEL.INFO,
    startAt: {
      found: false,
      literal: '',
    },
    testsOnly: false,
  };

  const directory = path.join(root, 'aspects');

  const entries = await fs.readdir(directory, {withFileTypes: true});

  const aspects: Array<[string, string]> = [];

  for (const entry of entries) {
    if (entry.isDirectory()) {
      const name = entry.name;

      const {description} = await readAspect(
        path.join(directory, name, 'aspect.json')
      );

      aspects.push([name, description]);
    }
  }

  for (const arg of args) {
    if (arg === '--debug' || arg === '-d') {
      options.logLevel = LOG_LEVEL.DEBUG;
    } else if (arg === '--quiet' || arg === '-q') {
      options.logLevel = LOG_LEVEL.NOTICE;
    } else if (arg === '--test' || arg === '-t') {
      options.testsOnly = true;
    } else if (arg === '--help' || arg === '-h') {
      await printUsage(aspects);
      throw new ErrorWithMetadata('aborting');
    } else if (arg.startsWith('--start-at-task=')) {
      options.startAt.literal = (
        arg.match(/^--start-at-task=(.*)/)?.[1] ?? ''
      ).trim();
      options.startAt.fuzzy = new RegExp(
        [
          '',
          ...options.startAt.literal.split(/\s+/).map(escapeRegExpPattern),
          '',
        ].join('.*'),
        'i'
      );
    } else if (arg.startsWith('-')) {
      throw new ErrorWithMetadata(`unrecognized argument ${arg}`);
    } else {
      // TODO: error for bad aspects
    }
  }

  return options;
}

async function printUsage(aspects: Array<[string, string]>) {
  // TODO: actually implement all the switches mentioned here
  log(
    dedent`

      ./install [options] [aspects...]

      ${bold`Options:`}

        -c/--check # not yet implemented
        -d/--debug
        -f/--force # not yet implemented
        -h/--help
        -q/--quiet
        -t/--test
        -v/--verbose (repeat up to four times for more verbosity) # not yet implemented
        --start-at-task='aspect | task' # TODO: maybe make -s short variant
        --step # not yet implemented

      ${bold`Aspects:`}
    `
  );

  for (const [aspect, description] of aspects) {
    log(`  ${aspect}`);
    log(`    ${description}`);
  }
{'fraction_non_alphanumeric': 0.16833421098823942, 'fraction_numerical': 0.00035106196243637, 'mean_word_length': 2.4263379434756462, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1730115', 'n_tokens_mistral': 1818, 'n_tokens_neox': 1789, 'n_words': 481}