Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 24 additions & 7 deletions PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,16 @@ manpages, and SDKs — never a runtime dependency of somebody else's program.

### Next: the derive

- [ ] **Static metadata tables** — a second, cold table holding what the hot one
- [x] **Static metadata tables** — a second, cold tree holding what the hot one
deliberately omits: help and long help, about text, hidden-ness, visible and
hidden aliases, value names and hints, `choices`, defaults, `env`, effects,
mounts, examples. Separate from the parse tables so the hot path keeps its
cache locality.
- [ ] **KDL emission** — write the spec from those tables without depending on
usage-lib, which pulls kdl, miette, tera, and regex. Verified by
round-tripping the output through `Spec::from_str` and comparing.
hidden aliases, value names, `choices`, defaults, `env`, effects, mounts,
restart tokens, examples. Behind the `spec` feature, and each entry borrows
the parse-table entry it describes, so names have one definition and cannot
drift.
- [x] **KDL emission** — `Spec::to_kdl`, written by hand so the crate keeps having
no dependencies. Verified by parsing the output back with usage-lib and
checking the resulting spec field by field, then rendering it through the
markdown and manpage generators an adopter's docs build actually uses.
- [ ] **`usage-derive` v0** — flags, positionals, subcommands, doc-comment help
(first paragraph short, whole block long), spec emission. Usage-native
attributes mirroring the KDL vocabulary rather than a clap dialect.
Expand All @@ -117,6 +119,21 @@ manpages, and SDKs — never a runtime dependency of somebody else's program.
they belong with the derive rather than in the parser. Closes the 24 corpus
vectors usage-argv reports as out of scope.

### Spec gaps found on the way

Each of these is a thing a CLI wants to say that the spec has no way to record.
Per the canonicality rule the spec gets extended first, so these block the derive
carrying them rather than being worked around.

- [ ] **`help_heading`** — grouping flags under a heading in help output. clap has
it and mise uses it (in `watch`, for its vendored watchexec arguments), and
the metadata tree deliberately omits it rather than dropping it silently on
the way out.
- [ ] **A mount on the root command** — the spec accepts `mount` only inside a
`cmd` block, so a CLI whose _top-level_ subcommands are discovered by running
something cannot say so. Worth deciding whether that is a gap or a deliberate
restriction.

### Then: what a CLI framework has to have

- [ ] **Help rendering** — `--help` and `-h` from the static metadata, with
Expand Down
5 changes: 5 additions & 0 deletions argv/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ license = { workspace = true }
# invocation of every CLI built on it.
[dependencies]

[features]
# Cold-path metadata and spec emission. Off by default so a CLI that wants only
# a parser does not carry it.
spec = []

[package.metadata.release]
shared-version = true
release = true
10 changes: 10 additions & 0 deletions argv/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,22 @@
//! know a value's type, so they belong to the layer that owns the target struct.
//! Keeping them out is what makes this loop small.
//!
//! # Features
//!
//! - `spec` — a parallel tree of cold metadata (help text, choices, defaults,
//! effects) and a writer that emits it as a usage spec. Off by default: a
//! successful parse never reads any of it, so a CLI that only wants a parser
//! should not compile it.
//!
//! [the argv grammar]: https://usage.jdx.dev/spec/argv

#![forbid(unsafe_code)]

use std::ffi::OsStr;

#[cfg(feature = "spec")]
pub mod spec;

/// How deep a command tree this parser will descend.
///
/// The ancestor chain is kept in a fixed-size array so that a parse allocates
Expand Down
Loading