All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.projectnessie.nessie.cli.commands.HelpCommand Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2024 Dremio
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.projectnessie.nessie.cli.commands;

import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Arrays.asList;
import static org.projectnessie.nessie.cli.cli.BaseNessieCli.STYLE_COMMAND_NAME;
import static org.projectnessie.nessie.cli.cli.BaseNessieCli.STYLE_HEADING;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Deque;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.jline.utils.AttributedString;
import org.jline.utils.AttributedStringBuilder;
import org.projectnessie.nessie.cli.cli.BaseNessieCli;
import org.projectnessie.nessie.cli.cmdspec.CommandSpec;
import org.projectnessie.nessie.cli.cmdspec.CommandType;
import org.projectnessie.nessie.cli.cmdspec.HelpCommandSpec;
import org.projectnessie.nessie.cli.grammar.Node;
import org.projectnessie.nessie.cli.grammar.Token;

public class HelpCommand extends NessieListingCommand {
  public HelpCommand() {}

  @Override
  protected Stream executeListing(BaseNessieCli cli, HelpCommandSpec spec) {
    List args = spec.getArguments();

    List argTypes = args.stream().map(Node::getType).collect(Collectors.toList());

    if (argTypes.size() == 1) {
      if (argTypes.get(0) == Token.TokenType.LICENSE) {
        return licenseInfo();
      }
    }

    List matches =
        Arrays.stream(CommandType.values())
            .filter(
                t -> {
                  NessieCommand c = CommandsFactory.buildCommandInstance(t);
                  return args.isEmpty() || c.matchesNodeTypes().contains(argTypes);
                })
            .collect(Collectors.toList());

    Stream output;

    if (matches.size() == 1) {
      output = exactMatch(cli, matches.get(0));
    } else {
      output = listAll(cli, matches);
    }

    return output;
  }

  private static Stream licenseInfo() {
    Stream lines = Stream.of("", "Nessie LICENSE", "==============", "");

    lines =
        Stream.concat(
            lines, Arrays.stream(fetchResource("META-INF/resources/LICENSE.txt").split("\n")));

    lines = Stream.concat(lines, Stream.of("", "", "Nessie NOTICE", "=============", ""));
    lines =
        Stream.concat(
            lines, Arrays.stream(fetchResource("META-INF/resources/NOTICE.txt").split("\n")));

    return lines;
  }

  private static Stream listAll(BaseNessieCli cli, List matches) {
    Stream output;

    output =
        matches.stream()
            .map(CommandsFactory::buildCommandInstance)
            .flatMap(
                c ->
                    Stream.of(
                        new AttributedString(c.name(), STYLE_COMMAND_NAME).toAnsi(cli.terminal()),
                        c.description(),
                        ""));

    Stream heading = Stream.of("", "Nessie CLI - Help", "");
    output = Stream.concat(heading, output);
    return output;
  }

  private Stream exactMatch(BaseNessieCli cli, CommandType commandType) {

    NessieCommand c = CommandsFactory.buildCommandInstance(commandType);

    Stream heading =
        Stream.of(
            "",
            new AttributedStringBuilder()
                .append("Nessie CLI - ")
                .append(c.name(), STYLE_COMMAND_NAME)
                .append(" - ")
                .append(c.description())
                .toAnsi(cli.terminal()));

    String extensionPlain = ".plain.txt";
    String extension = cli.dumbTerminal() ? extensionPlain : ".ansi.txt";

    Stream syntax =
        Stream.of(new AttributedString("Syntax:", STYLE_HEADING).toAnsi(cli.terminal()), "");

    for (String nonTerminalRef : nonTerminalRefs(commandType.statementName())) {
      // .trim() would remove trailing ESC chars as well, but we need those for proper ANSI output.
      String refSyntax =
          fetchSyntaxHelp(nonTerminalRef + extension).replaceAll("^\\s", "").replaceAll("\\s$", "");
      String refSyntaxPlain = fetchSyntaxHelp(nonTerminalRef + extensionPlain).trim();

      boolean isCommand = commandType.statementName().equals(nonTerminalRef);

      String help = fetchSyntaxHelp(nonTerminalRef + ".help.txt").trim();

      boolean showSyntax =
          !refSyntaxPlain.isEmpty()
              && !refSyntaxPlain.endsWith("Identifier")
              && !refSyntaxPlain.endsWith("PositiveInteger");
      boolean showHelp = !help.isEmpty();

      if (!isCommand && (showSyntax || showHelp)) {
        syntax =
            Stream.concat(
                syntax,
                Stream.of(
                    "\n"
                        + new AttributedString(nonTerminalRef + ":", STYLE_HEADING)
                            .toAnsi(cli.terminal())
                        + "\n"));
      }

      if (showSyntax) {
        syntax = Stream.concat(syntax, indent(refSyntax));
      }

      if (showHelp) {
        syntax =
            Stream.concat(
                syntax,
                isCommand
                    ? Stream.of(
                        "",
                        new AttributedString("Description:", STYLE_HEADING).toAnsi(cli.terminal()),
                        "",
                        help)
                    : Stream.of(help));
      }
    }

    syntax = Stream.concat(syntax, Stream.of(""));

    return Stream.concat(heading, syntax);
  }

  private static Stream indent(String txt) {
    return Arrays.stream(txt.split("\n")).map(s -> "    " + s);
  }

  private static Set nonTerminalRefs(String nonTerminalName) {
    Set allRefs = new LinkedHashSet<>();

    Deque deque = new ArrayDeque<>();
    deque.addLast(nonTerminalName);
    while (!deque.isEmpty()) {
      String name = deque.removeFirst();
      if (allRefs.add(name)) {
        deque.addAll(asList(fetchSyntaxHelp(nonTerminalName + ".refs").split("\n")));
      }
    }

    return allRefs;
  }

  private static String fetchSyntaxHelp(String resource) {
    return fetchResource("org/projectnessie/nessie/cli/syntax/" + resource);
  }

  private static String fetchResource(String resource) {
    URL url = HelpCommand.class.getClassLoader().getResource(resource);
    if (url == null) {
      return "";
    }
    try (InputStream in = url.openConnection().getInputStream()) {
      return new String(in.readAllBytes(), UTF_8);
    } catch (IOException e) {
      throw new RuntimeException("Failed to load resource " + url, e);
    }
  }

  public String name() {
    return Token.TokenType.HELP.name();
  }

  public String description() {
    return "Prints help information.";
  }

  @Override
  public List> matchesNodeTypes() {
    return List.of(List.of(Token.TokenType.HELP));
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy