package me.dinowernli.grpc.polyglot.config;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.Option;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
/** Provides easy access to the arguments passed on the command line. */
public class CommandLineArgs {
@Option(name = "--full_method", metaVar = "<some.package.Service/doSomething>")
private String fullMethodArg;
@Option(name = "--endpoint", metaVar = "<host>:<port>")
private String endpointArg;
@Option(name = "--config_set_path", metaVar = "<path/to/config.pb.json>")
private String configSetPathArg;
@Option(name = "--config_name", metaVar = "<config-name>")
private String configNameArg;
// The flags below represent overrides for the configuration used at runtime.
@Option(name = "--output_file_path", metaVar = "<path>")
private String outputFilePathArg;
@Option(name = "--use_tls", metaVar = "true|false")
private String useTlsArg;
@Option(name = "--add_protoc_includes", metaVar = "<path1>,<path2>")
private String addProtocIncludesArg;
@Option(name = "--proto_discovery_root", metaVar = "<path>")
private String protoDiscoveryRootArg;
@Option(name = "--deadline_ms", metaVar = "<number>")
private Integer deadlineMs;
@Option(name = "--tls_ca_cert_path", metaVar = "<path>")
private String tlsCaCertPath;
@Option(name = "--help")
private Boolean help;
// *************************************************************************
// * Initial step towards the migration to "polyglot <command> [flagz...]" *
// *************************************************************************
/** Command to make a GRPC call to an endpoint */
public static final String CALL_COMMAND = "call";
/** Command to list all known services defined in the proto files*/
public static final String LIST_SERVICES_COMMAND = "list_services";
@Option(name = "--command", metaVar = "<call|list_services>")
private String commandArg;
// TODO: Move to a "list_services"-specific flag container
@Option(
name = "--service_filter",
metaVar = "service_name",
usage="Filters service names containing this string e.g. --service_filter TestService")
private String serviceFilterArg;
// TODO: Move to a "list_services"-specific flag container
@Option(
name = "--method_filter",
metaVar = "method_name",
usage="Filters service methods to those containing this string e.g. --method_name List")
private String methodFilterArg;
//TODO: Move to a "list_services"-specific flag container
@Option(
name = "--with_message",
metaVar = "true|false",
usage="If true, then the message specification for the method is rendered")
private String withMessageArg;
// *************************************************************************
/**
* Parses the arguments from the supplied array. Throws {@link IllegalArgumentException} if the
* supplied array is malformed.
*/
public static CommandLineArgs parse(String[] args) {
CommandLineArgs result = new CommandLineArgs();
CmdLineParser parser = new CmdLineParser(result);
try {
parser.parseArgument(args);
} catch (CmdLineException e) {
throw new IllegalArgumentException("Unable to parse command line flags", e);
}
return result;
}
/** Returns a single-line usage string explaining how to pass the command line arguments. */
public static String getUsage() {
CommandLineArgs result = new CommandLineArgs();
CmdLineParser parser = new CmdLineParser(result);
OutputStream stream = new ByteArrayOutputStream();
parser.printSingleLineUsage(stream);
return stream.toString();
}
private CommandLineArgs() {
}
/** Returns the endpoint string */
public Optional<String >endpoint() {
return Optional.ofNullable(endpointArg);
}
/** Returns the endpoint method */
public Optional<String >fullMethod() {
return Optional.ofNullable(fullMethodArg);
}
/** Returns the root of the directory tree in which to discover proto files. */
public Optional<Path> protoDiscoveryRoot() {
return maybePath(protoDiscoveryRootArg);
}
/** Returns the location in which to store the response proto. */
public Optional<Path> outputFilePath() {
return maybePath(outputFilePathArg);
}
public Optional<Boolean> useTls() {
if (useTlsArg == null) {
return Optional.empty();
}
return Optional.of(Boolean.parseBoolean(useTlsArg));
}
public Optional<Path> configSetPath() {
return maybePath(configSetPathArg);
}
public Optional<String> configName() {
return Optional.ofNullable(configNameArg);
}
public Optional<Path> tlsCaCertPath() {
return maybePath(tlsCaCertPath);
}
/**
* First stage of a migration towards a "command"-based instantiation of polyglot.
* Supported commands:
* list_services [--service_filter XXX] [--method_filter YYY]
*/
public Optional<String> command() {
return Optional.ofNullable(commandArg);
}
// **********************************************
// * Flags supporting the list_services command *
// **********************************************
// TODO: Move to a "list_services"-specific flag container
public Optional<String> serviceFilter() {
return Optional.ofNullable(serviceFilterArg);
}
// TODO: Move to a "list_services"-specific flag container
public Optional<String> methodFilter() {
return Optional.ofNullable(methodFilterArg);
}
//TODO: Move to a "list_services"-specific flag container
public Optional<Boolean> withMessage() {
if (withMessageArg == null) {
return Optional.empty();
}
return Optional.of(Boolean.parseBoolean(withMessageArg));
}
// *************************************************************************
public ImmutableList<Path> additionalProtocIncludes() {
if (addProtocIncludesArg == null) {
return ImmutableList.of();
}
ImmutableList.Builder<Path> resultBuilder = ImmutableList.builder();
for (String pathString : addProtocIncludesArg.split(",")) {
Path includePath = Paths.get(pathString);
Preconditions.checkArgument(Files.exists(includePath), "Invalid include: " + includePath);
resultBuilder.add(includePath);
}
return resultBuilder.build();
}
public Optional<Integer> getRpcDeadlineMs() {
return Optional.ofNullable(deadlineMs);
}
public boolean isHelp() {
return help != null && help;
}
private static Optional<Path> maybePath(String rawPath) {
if (rawPath == null) {
return Optional.empty();
}
Path path = Paths.get(rawPath);
Preconditions.checkArgument(Files.exists(path), "File " + rawPath + " does not exist");
return Optional.of(Paths.get(rawPath));
}
}