ArgHelp.java

package net.morimekta.terminal.args;

import net.morimekta.io.tty.TTY;
import net.morimekta.terminal.args.impl.ArgHelpImpl;

import java.io.PrintStream;
import java.util.Comparator;

import static java.util.Objects.requireNonNull;

/**
 * Helper class to display help and usage for a program or sub-command.
 */
public interface ArgHelp {
    /**
     * @param parser The argument parser to build help helper for.
     * @return The argument help builder.
     */
    static Builder argHelp(ArgParser parser) {
        return new ArgHelpImpl.BuilderImpl(parser);
    }

    /**
     * Print the single line usage string to print stream. Contains a short-name
     * flag list and a list of options and arguments. This will always be on a
     * single line when called with this method.
     * <p>
     * Example single line usage: <code>[-sh] [--long] [--long L] [arg]</code>
     *
     * @param writer The print stream to write single line usage to.
     */
    void printSingleLineUsage(PrintStream writer);

    /**
     * Print the preamble for the help with short options. Handy if you want to
     * explain why some option was invalid, missing etc. Note that the single
     * line usage will be formatted and wrapped to fit the usage width limit.
     * <p>
     * Example preamble with short overview of the program ant it's options:
     * <pre>{@code
     * {program description} - {version}
     * Usage: {program} {single line usage}
     * }</pre>
     *
     * @param writer The print stream to write preamble to.
     */
    void printPreamble(PrintStream writer);

    /**
     * Print the help output for the argument parser, starting with base
     * information about the program, the single line usage, and an overview
     * over options, arguments and sub-commands. It will not print detailed
     * options for sub-commands. To do that make an arg helper for the sub-
     * -command parser, and print help for that. Note that the single line
     * usage will be formatted and wrapped to fit the usage width limit.
     * <p>
     * Example help:
     * <pre>{@code
     * {program description} - {version}
     * Usage: {program} {single line usage}
     *
     *  {--longName} {metaVar} : {usage string} (default: {default})
     * }</pre>
     * <p>
     * In order to print help for a sub-command do this:
     * <pre>{@code
     * argHelp(argParser.getSubCommandSet()
     *                  .parserForSubCommand("name"))
     *         .printHelp(System.out);
     * }</pre>
     *
     * @param writer The print stream to write help to.
     * @see SubCommandSet#parserForSubCommand(String)
     */
    void printHelp(PrintStream writer);

    /**
     * Builder for customizing printed help output.
     */
    interface Builder {
        /**
         * @return The built helper.
         */
        ArgHelp build();

        /**
         * @param prefix The prefix to show before the single line usage in the
         *               preamble. Defaults to <code>Usage: </code>. Should end
         *               with a space if present, otherwise there will be no
         *               space between the prefix and the program name.
         * @return The builder.
         */
        Builder singleLineUsagePrefix(String prefix);

        /**
         * @param comparator Comparator to sort options by when displaying help.
         *                   Defaults to not sorted, so printed in order they
         *                   were added to the {@link ArgParser.Builder}.
         * @return The builder.
         */
        Builder withOptionsComparator(Comparator<Option> comparator);

        /**
         * @param show If default values should be shown. Defaults to true.
         * @return The builder.
         */
        Builder showDefaults(boolean show);

        /**
         * @param show If hidden options should be shown. Defaults to false.
         * @return The builder.
         */
        Builder showHiddenOptions(boolean show);

        /**
         * @param show If hidden arguments should be shown. Defaults to false.
         * @return The builder.
         */
        Builder showHiddenArguments(boolean show);

        /**
         * @param show If hidden sub-commands should be shown. Defaults to false.
         * @return The builder.
         */
        Builder showHiddenSubCommands(boolean show);

        /**
         * @param show If sub-commands overview should be shown. Defaults to true.
         * @return The builder.
         */
        Builder showSubCommands(boolean show);

        /**
         * @param header Header string to show between option / argument overview
         *               and the sub-command overview.
         * @return The builder.
         */
        Builder subCommandsHeader(String header);

        /**
         * @param width Wrap usage description at this many characters width.
         * @return The builder.
         */
        Builder usageWidth(int width);

        /**
         * @param tty Set usage with from the terminal width of the TTY.
         * @return The builder.
         */
        default Builder usingTTYWidth(TTY tty) {
            return usingTTYWidth(tty, Integer.MAX_VALUE);
        }

        /**
         * Set usage with from the terminal width of the TTY but only up to
         * a maximum.
         *
         * @param tty      Set usage with from the terminal width of the TTY.
         * @param maxWidth Max width of usage wrapping.
         * @return The builder.
         */
        default Builder usingTTYWidth(TTY tty, int maxWidth) {
            requireNonNull(tty, "tty == null");
            if (maxWidth < 1) {
                throw new IllegalArgumentException("Invalid max width " + maxWidth);
            }
            return usageWidth(Math.min(tty.getTerminalSize().cols, maxWidth));
        }

        /**
         * @param show If hidden options, arguments and sub-commands should be
         *             shown. Defaults to false.
         * @return The builder.
         */
        default Builder showHidden(boolean show) {
            return showHiddenOptions(show)
                    .showHiddenArguments(show)
                    .showHiddenSubCommands(show);
        }

        /**
         * Build and print the single line usage.
         * See {@link ArgHelp#printSingleLineUsage(PrintStream)}
         *
         * @param writer The stream to write to.
         */
        default void printSingleLineUsage(PrintStream writer) {
            build().printSingleLineUsage(writer);
        }

        /**
         * Build and print the help preamble.
         * See {@link ArgHelp#printPreamble(PrintStream)}
         *
         * @param writer The stream to write to.
         */
        default void printPreamble(PrintStream writer) {
            build().printPreamble(writer);
        }

        /**
         * Build and print help overview.
         * See {@link ArgHelp#printHelp(PrintStream)}
         *
         * @param writer The stream to write to.
         */
        default void printHelp(PrintStream writer) {
            build().printHelp(writer);
        }
    }
}