Options Classes

Bundle related options into reusable classes with @Options, compose them across commands, and use @GlobalOptions.

As CLI tools grow, groups of related options tend to appear together across multiple commands. Rather than duplicating the same @Option parameters on every method, Crest lets you bundle them into a reusable class annotated with @Options.

Defining an Options Class

An @Options class defines its options through its constructor parameters. Each constructor parameter uses the same @Option, @Default, and @Required annotations as regular command parameters.

@Options
public class Config {
    private final String name;
    private final String env;

    public Config(@Option("config") @Default("default") final String name,
                  @Option("env") @Default("prod") final String env) {
        this.name = name;
        this.env = env;
    }

    public String getName() { return name; }
    public String getEnv() { return env; }
}
@Options
public class CustomerIds {
    private final List<String> customerIds;
    private final File customerIdFile;

    public CustomerIds(@Option("customer-id") final List<String> customerIds,
                       @Option("customers") final File customerIdFile) {
        this.customerIds = customerIds;
        this.customerIdFile = customerIdFile;
    }

    public List<String> getCustomerIds() { return customerIds; }
    public File getCustomerIdFile() { return customerIdFile; }
}

Using Options in Commands

Inject an @Options class into a command as a plain parameter – no annotation needed on the parameter itself. Crest recognizes the type and expands its options into the command’s option set.

@Command
public void deploy(final Config config,
                   final CustomerIds customers,
                   @Option("version") final String version) { ... }

CLI usage: deploy --config=staging --env=dev --customer-id=acme --version=2.1.0

The --config, --env, and --customer-id options come from the Config and CustomerIds classes, while --version is defined directly on the method.

Composing Multiple Options

A single command can accept any number of @Options classes. This lets you compose option sets freely:

@Command
public void upload(final Config config,
                   final CustomerIds customers,
                   @Option("dry-run") @Default("true") final Boolean dryRun) { ... }

@Command
public void download(final Config config,
                     @Option("output") final File outputDir) { ... }

Both commands share the Config options, but only upload includes CustomerIds. Each command gets a clean, focused signature while reusing common option definitions.

Constructor Parameters

The constructor is the only place to define options in an @Options class. Crest calls the constructor with the parsed option values, so the class can validate and store them however it likes:

@Options
public class DateRange {
    private final LocalDate start;
    private final LocalDate end;

    public DateRange(@Option("start") @Required final LocalDate start,
                     @Option("end") @Required final LocalDate end) {
        if (end.isBefore(start)) {
            throw new IllegalArgumentException("End date must be after start date");
        }
        this.start = start;
        this.end = end;
    }

    public LocalDate getStart() { return start; }
    public LocalDate getEnd() { return end; }
}

Nillable Options

By default, Crest always constructs the @Options object, even if the user provides none of its options. Use nillable = true to allow the object to be null when no values are provided:

@Options(nillable = true)
public class Pagination {
    public Pagination(@Option("page") final int page,
                      @Option("size") @Default("20") final int size) { ... }
}
@Command
public void list(final Pagination pagination,
                 @Option("filter") final String filter) {
    if (pagination == null) {
        // No pagination options were provided
    }
}

This is useful when the presence or absence of an option group is meaningful.

@GlobalOptions

@GlobalOptions works the same as @Options, but the options are automatically available to every command without needing to add the class as a parameter.

@GlobalOptions
public class Verbosity {
    public Verbosity(@Option("verbose") @Default("false") final boolean verbose,
                     @Option("quiet") @Default("false") final boolean quiet) { ... }
}

With @GlobalOptions, every command in the application automatically recognizes --verbose and --quiet without explicitly declaring a Verbosity parameter.