promptUntilValidPickSync<T extends Object> function

T promptUntilValidPickSync<T extends Object>({
  1. required String prompt,
  2. required Set<T> options,
  3. required StringifiedPromptOption stringify(
    1. int index,
    2. T option
    ),
  4. String onError(
    1. String input
    )?,
  5. String selectorFormatter(
    1. String selector
    )?,
  6. T? defaultValue,
  7. String separator = ")",
  8. String promptIndent = "",
  9. String optionsIndent = "\t",
  10. String errorIndent = "",
})

Notice

This function blocks until a full line is available. If you do not want to block the current thread, use the async version instead.

See promptUntilValidPick

Description

Prompt the user to pick one of the values from options. This function only returns, if the user inputs a valid selector.

stringify transforms each value from options into a representable string and associates a selector with the value.

Each selector must fulfill the following requirements:

  • it must be unique

  • it must not be a blank string (if you want to assign an option to a blank string use defaultValue)

  • it must not contain any linefeeds (\n)

If the user inputs an invalid selector, then the result of onError gets written to stdout and the user gets prompted again until they input a valid selector. The default function for onError informs the user that they picked an invalid selector.

selectorFormatter can be used to customize the representation of the selector. For example, this function can apply ANSI Escape Codes to affect the appearance of the selector. By default, no formatting is applied to a selector.

separator is the string between each selector and its associated option. A whitespace gets always appended to the separator.

See promptUntilValidPick.

Implementation

T promptUntilValidPickSync<T extends Object>({
  required String prompt,
  required Set<T> options,
  required StringifiedPromptOption Function(int index, T option) stringify,
  String Function(String input)? onError,
  String Function(String selector)? selectorFormatter,
  T? defaultValue,
  String separator = ")",
  String promptIndent = "",
  String optionsIndent = "\t",
  String errorIndent = "",
}) {
  onError ??= (input) {
    return input.isBlank
        ? "You must pick an option"
        : "There is no option for this value: $input";
  };
  selectorFormatter ??= (selector) => selector;

  final selectorOptionsMap = <String, T>{};
  final optionsStrBuf = IndentedStringBuffer(indent: optionsIndent);

  void checkIsValidSelector(String selector) {
    if (selector.isBlank)
      throw ResultError(
        selector,
        functionName: "transform",
        description: "A selector must not be a blank string. If you want to "
            "assign an option to a blank string use 'defaultValue'.",
      );

    if (selector.contains("\n"))
      throw ResultError(
        selector,
        functionName: "transform",
        description: "A selector must not contain any linefeeds.",
      );

    if (selectorOptionsMap.containsKey(selector))
      throw ResultError(
        selector,
        functionName: "transform",
        description:
            "The provided selector already exists. Each selector must be "
            "unique: $selector",
      );
  }

  var i = 0;
  for (final item in options) {
    final (selector: selector, option: stringifiedOption) = stringify(i, item);

    checkIsValidSelector(selector);
    selectorOptionsMap[selector] = item;

    optionsStrBuf.writeIndented(selectorFormatter(selector));
    optionsStrBuf.write("$separator ");
    optionsStrBuf.write(stringifiedOption);
    if (i < options.length - 1) {
      optionsStrBuf.writeln();
    }

    i++;
  }

  final optionsStr = optionsStrBuf.toString();

  stdout.write(promptIndent);
  stdout.writeln(prompt);

  stdout.writeln(optionsStr);

  while (true) {
    final answer = stdin.readLineSync()?.trim() ?? "";
    if (null != defaultValue && answer.isBlank) return defaultValue;

    final result = selectorOptionsMap[answer];
    if (null != result) return result;

    stdout.writeln();
    stdout.write(errorIndent);
    stdout.writeln(onError(answer));

    stdout.writeln(optionsStr);
  }
}