qsv_docopt 1.10.0

Command line argument parsing.
Documentation
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project purpose

`qsv_docopt` is a fork of the unmaintained [docopt.rs](https://github.com/docopt/docopt.rs), maintained for the [qsv](https://github.com/jqnatividad/qsv) CLI. Docopt derives the argument parser from the self-documenting usage string, which is what qsv relies on and which `clap`/`structopt` cannot replicate. The crate ships a library (`qsv_docopt`) and a small binary (`docopt-wordlist`, `src/wordlist.rs`) used for bash tab-completion.

## Commands

```bash
cargo build              # debug build
cargo build --release
cargo test               # full suite, including generated testcases
cargo test <substring>   # run a single test by name substring

# Formatting requires nightly — rustfmt.toml uses nightly-only options
# (wrap_comments, format_strings, group_imports, imports_granularity,
# *_align_threshold). Stable `cargo fmt` will not apply project style.
cargo +nightly fmt

# Regenerate src/test/testcases.rs after editing src/test/testcases.docopt.
# testcases.rs is a checked-in generated artifact; CI does not regenerate it,
# so a stale .rs silently tests the old fixtures.
./scripts/mk-testcases ./src/test/testcases.docopt > ./src/test/testcases.rs
# or: make src/test/testcases.rs
```

MSRV: Rust 1.95, edition 2024 (see `Cargo.toml`). CI (`.github/workflows/{linux,macos,windows}.yml`) only runs `cargo build --verbose` and `cargo test --verbose` — there is no lint or format gate.

## Architecture

- `src/lib.rs` — crate entry, module declarations, crate-level doc examples.
- `src/dopt.rs` — public API surface. `Docopt` builder (`new``argv``parse`/`deserialize`), `ArgvMap` (untyped result, accessed with `get_bool` / `get_count` / `get_str` / `get_vec`), `Value` (`Switch` / `Counted` / `Plain` / `List`), `Error`, and the serde `Deserializer` impl that powers typed `.deserialize()`.
- `src/parse.rs` — docopt grammar. `Parser` builds a `Pattern` tree of `Atom`s from the usage string; `Matcher` + `MState` walk an `Argv` stream against that tree. Edits here change how all usage strings are interpreted.
- `src/synonym.rs``SynonymMap` backs option synonyms (e.g. `-a``--archive`) so lookups by either spelling resolve to the same value. The crate uses `ahash::AHashMap` throughout; follow suit rather than reaching for `std::collections::HashMap`.
- `src/utils.rs` — shared helpers.
- `src/wordlist.rs` — the `docopt-wordlist` binary.
- `src/test/``mod.rs` plus `suggestions.rs` hold hand-written tests; `testcases.docopt` is the fixture source and `testcases.rs` is its generated companion (do not hand-edit).

## Non-obvious conventions

- **Struct field naming for `#[derive(Deserialize)]` targets.** `--flag` / `-f``flag_flag`; `<arg>` or `ARG``arg_arg` / `arg_ARG`; a bare command token `build``cmd_build`. Getting the prefix wrong yields a silent field-miss at runtime, not a compile error.
- **Argument ingestion is lossy-UTF-8.** The crate reads `std::env::args_os()` and replaces non-UTF-8 bytes with U+FFFD instead of panicking. Callers that need byte-exact argv must build the vector themselves and pass it via `.argv(...)`.
- **Two parallel result APIs.** `.parse()` returns an untyped `ArgvMap`; `.deserialize()` returns a user struct via serde. Both share the `Error` type and the `.unwrap_or_else(|e| e.exit())` exit idiom. Match whichever style the surrounding code and tests already use.