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.17 at Testing

Caveats

  • Option names are the class's simple name in lowercase: Root becomes --root and -r. The first registration wins a short-name collision; -h is 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.

architecture.svg
architecture.svg
classes.svg
classes.svg
packages.svg
packages.svg