# eldritch

`software.visionary:eldritch:2026.10.13` · https://codeberg.org/javanese/eldritch

Visionary Software Solutions Eldritch: command-line arguments read by type, their arity checked, for work a main method or a ToolProvider runs

What a command's work is handed, whether a <code>main</code> method or a <code>ToolProvider</code> runs it: its arguments, read by type with their arity checked, and <code>err</code> to report on.

## Rationale

**Why.** argumentative's <code>Arguments</code> keeps every argument a line wrote, duplicates included, and leaves counting them to the caller.

## Key Concepts

### TypedArguments

**Role.** A command line's arguments, each read by its type: <code>required</code>, <code>optional</code>, <code>oneOf</code> and <code>has</code> check the arity its kind declares. Implementing <code>args()</code> is all it takes.

**Pattern.** Template Method

### Invocation

**Role.** The seam between a command's work and what runs it, a <code>main</code> method or a <code>ToolProvider</code>: <code>TypedArguments</code>, with <code>emitProgress</code> to report on <code>err</code>, and <code>complain</code> to report on <code>err</code> and make the run exit 1 without stopping the work.

## How It Works

**Guarantees.** A <code>Required</code> left out, or any argument written twice, is refused with an <code>IllegalArgumentException</code> naming its long option: <code>--root is required</code>. A <code>oneOf</code> with no leg written, or more than one, is refused naming every leg: <code>exactly one of (--left | --right) is written, and this line writes 0</code>. Reading an argument the command does not accept is an <code>IllegalStateException</code>: a defect in the command, never in its line.

## Lane

**Purpose.** Turning a command line's strings into typed arguments, refused where they are read.

**Alternative.** argumentative's <code>Arguments</code> alone, streamed, when a command counts its own arguments.

## Usage

Without eldritch: the caller counts each argument

```java
final List<Root> roots = StreamSupport.stream(arguments.spliterator(), false)
        .filter(Root.class::isInstance).map(Root.class::cast).toList();
if (roots.size() != 1) {
    throw new IllegalArgumentException("--root is required, once");
}
final Path root = roots.get(0).value();

```

With eldritch: each argument is read by its type, and the work reports how it goes

```java
final Path root = invocation.required(Root.class);
if (invocation.has(Verbose.class)) {
    invocation.emitProgress("reading " + root);
}

```

## Dependencies

- `software.visionary:argumentative:2026.10.13` at Compile
- `software.visionary:junit-closure:2026.09.17` at Testing

