argumentative
Visionary Software Solutions Argumentative: typed command-line argument parsing and marshalling
A command-line parser and marshaller that converts String[] into application-defined Java types, with generated usage text and no runtime dependencies beyond the JDK.
Rationale
Why. Each value is built by a converter the program supplies, so the compiler checks construction and nothing is created by reflection. The parser needs only the JDK, so build tools and other low-level programs can use it without adding dependencies.
Problem. Parsing String[] by hand repeats each option's name in the parsing loop, the usage text and the error messages, and scatters value conversion through the program.
Solution. Register each option once, with its converter. Parsing, usage text and unknown-option errors all come from that registration, and the program receives typed values.
Key Concepts
Typed arguments
Role. An Argument<T> exposes its value through value() and Supplier.get(). Implement Flag for a boolean switch, or an Option<T> subtype for an option that takes a value: Required<T> declares exactly one occurrence, Additional<T> at most one, and OneOf<T> exactly one alternative. Records can implement these interfaces directly; a sealed interface groups the alternatives of a OneOf.
Registration and parsing
Role. Subclass Arguments and register accepted arguments through Products: flag(instance) for a flag, option(Type.class, converter) for an option with a value. Supplied converters control construction and validation. Registrations compose, so related commands can share options. Construction parses the input; iteration returns typed arguments in input order. accepts(Type.class) checks registration, and usage() formats required, optional and mutually exclusive options.
Path lists
Role. ArgFile represents an optional list of paths. Its listed(filename) helper reads one path per line, ignoring surrounding whitespace, blank lines and # comment lines. An unreadable file causes IllegalArgumentException.
How It Works
Guarantees. Unknown options and missing value tokens throw IllegalArgumentException during construction; conversion errors propagate from the converter. Parsing preserves every supplied occurrence, including duplicates. Enforcing the declared occurrence constraints is the caller's responsibility. Every parser recognizes --help, -h and a leading help; the caller handles displaying help.
Thread safety. The parsed argument list is immutable. Sharing it safely also requires thread-safe argument values and subclass state.
Lane
Purpose. Parsing command-line options into typed values for a Java program.
When to avoid. Command lines built on subcommands or positional arguments, such as git commit -m <message>.
Usage
Parse a path and a flag
import java.nio.file.Path;
import software.visionary.argumentative.Arguments;
import software.visionary.argumentative.Flag;
import software.visionary.argumentative.option.Required;
record Root(Path value) implements Required<Path> { }
record Verbose() implements Flag { }
final class Rooted extends Arguments {
Rooted(String... argv) {
super(argv, options -> options
.option(Root.class, text -> new Root(Path.of(text)))
.flag(new Verbose()));
}
}
var parsed = new Rooted("--root", "/src", "--verbose");
System.out.println(parsed.usage());
parsed.forEach(argument -> System.out.println(argument.value()));
Output
--root <root> [--verbose]
/src
true
Dependencies
software.visionary:junit-closure:2026.09.17at Testing
Caveats
- Option names are the class's simple name in lowercase:
Rootbecomes--rootand-r. The first registration wins a short-name collision;-his reserved for help. - Values occupy the next argv element, even if it starts with
-. Positional arguments,--name=value, combined short flags and the--terminator are unsupported.
Views
Drawn from the release. A link in a view opens its source at the release tag.