Skip to content

Instantly share code, notes, and snippets.

@AnixPasBesoin
Last active September 24, 2024 17:32
Show Gist options
  • Save AnixPasBesoin/b4d6446b9f0ab732a05786a38d46ce3a to your computer and use it in GitHub Desktop.
Save AnixPasBesoin/b4d6446b9f0ab732a05786a38d46ce3a to your computer and use it in GitHub Desktop.
BasiCli (reads "basically") is basically a very basic CLI library. In other words: it's a single Java class meant to be copy-pasted into your project with no additional dependencies...
import java.util.HashMap;
import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import static java.lang.String.format;
public class BasiCli {
// TODO: Add automatic help message generation
// TODO: Support abbreviated command params (-i, --input)
// TODO: Support boolean flags (requiring no value)
public static Builder builder() {
return new Builder();
}
private static class Param {
private Param(String name, boolean isMandatory, Predicate<String> validator) {
this.name = name;
this.isMandatory = isMandatory;
this.validator = validator;
}
private final String name;
private final boolean isMandatory;
private final Predicate<String> validator;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Param param = (Param) o;
return name.equals(param.name);
}
@Override
public int hashCode() {
return name.hashCode();
}
}
private BasiCli(Map<String, String> parsedParams) {
this.parsedParams = parsedParams;
}
private final Map<String, String> parsedParams;
public String getParamOrCrash(String paramName) {
if (!parsedParams.containsKey(paramName)) {
throw new IllegalArgumentException(format("Param %s not found", paramName));
}
return parsedParams.get(paramName);
}
public String getParamOrDefault(String paramName, String defaultValue) {
if (!parsedParams.containsKey(paramName)) {
return defaultValue;
}
return parsedParams.get(paramName);
}
public static class Builder {
private final Map<String, Param> paramsMap = new HashMap<>();
private Builder() {
// no-op
}
public Builder registerMandatoryParam(
String name,
Predicate<String> validator
) {
var param = new Param(name, true, validator);
paramsMap.put(name, param);
return this;
}
public Builder registerMandatoryParam(
String name
) {
var param = new Param(name, true, s -> true);
paramsMap.put(name, param);
return this;
}
public Builder registerOptionalParam(
String name,
Predicate<String> validator
) {
var param = new Param(name, false, validator);
paramsMap.put(name, param);
return this;
}
public Builder registerOptionalParam(
String name,
String defaultValue
) {
var param = new Param(name, false, s -> true);
paramsMap.put(name, param);
return this;
}
public BasiCli buildWithArgs(String[] args) {
var parsedParamsMap = new HashMap<String, String>();
if (args.length % 2 != 0) {
System.out.println("Currently supporting params with values only");
System.exit(-1);
}
for (var i = 0; i < args.length; i += 2) {
var arg = args[i];
if (!arg.startsWith("-")) {
System.out.println("Params should be in the form: -param");
System.exit(-1);
}
var optName = arg.replaceFirst("^-+", "");
var optValue = args[i + 1];
if (!this.paramsMap.containsKey(optName)) {
System.out.printf("Invalid param: -%s%n", optName);
System.exit(-1);
}
var opt = this.paramsMap.get(optName);
if (!opt.validator.test(optValue)) {
System.out.printf("Param %s has invalid value %s%n", optName, optValue);
System.exit(-1);
}
parsedParamsMap.put(optName, optValue);
}
var mandatoryParamNames = this.paramsMap.values()
.stream()
.filter(o -> o.isMandatory)
.map(o -> o.name)
.collect(Collectors.toSet());
var parsedParams = parsedParamsMap.keySet();
if (!parsedParams.containsAll(mandatoryParamNames)) {
System.out.println("At least one mandatory param is missing from the arguments");
System.exit(-1);
}
return new BasiCli(parsedParamsMap);
}
}
}
@AnixPasBesoin
Copy link
Author

AnixPasBesoin commented Oct 10, 2023

Usage

Create an instance of BasiCli and register command line parameters:

public class ExampleCli {
    // grab a builder instance of BasiCli
    private final static BasiCli.Builder BUILDER = BasiCli.builder()
            // register a mandatory CLI parameter "-a", and provide a validator predicate for it
            .registerMandatoryParam("a", s -> Integer.parseInt(s) > 0)
            // register an optional CLI parameter "-b" and provide a validator predicate for it as well
            .registerOptionalParam("b", s -> Integer.parseInt(s) > 0);

    public static void main(String[] args) {

        // parse the concrete arguments
        var basiCli = BUILDER.buildWithArgs(args);

        // get argument "-a"
        var a = basiCli.getParamOrCrash("a");
        // get argument "-b"
        var b = basiCli.getParamOrDefault("b", "3");

        // print arguments
        System.out.println(a);
        System.out.println(b);
    }
}

Test with the following command:

java ExampleCli.java -a 20 -b 5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment