feat: Validate + keypress subpackage; RFC-style design.md (v1.2.0)
Validate(root): optional startup/test check for tree authoring faults — >1 slot child per node, empty word, duplicate sibling words, dead-end node — traversing circular slots without looping (#3). keypress subpackage: WaitForKey(ctx, cancel) cancels a context on any keystroke for watch-style streaming commands, with per-GOOS cbreak (linux TCGETS/TCSETS, BSD TIOCGETA/TIOCSETA) and a non-tty/unsupported fallback that just waits on ctx. Lifts the last OpenBSD-specific bit out of evpnc/maglevc's watch.go (#6). docs: replace PROPOSAL.md with an RFC-2119 design.md (FR/NFR for the library). Example now dogfoods Validate (a unit test) and keypress (a bounded `watch` command). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -113,6 +113,35 @@ Both are additive conveniences: every builder method returns a plain
|
||||
`*cli.Node[C]`, so builder and struct-literal construction interoperate, and you
|
||||
can still drive the lifecycle yourself with `Shell`/`Dispatch` instead of `App`.
|
||||
|
||||
`cli.Validate(root)` reports common authoring faults (more than one slot child
|
||||
under a node, an empty word, duplicate sibling words, a dead-end node). It is
|
||||
optional; the idiomatic use is a one-line unit test so a malformed tree fails the
|
||||
build:
|
||||
|
||||
```go
|
||||
func TestTreeValid(t *testing.T) {
|
||||
if err := cli.Validate(buildTree()); err != nil { t.Fatal(err) }
|
||||
}
|
||||
```
|
||||
|
||||
## Streaming commands
|
||||
|
||||
For a `watch`-style command that streams until interrupted, the
|
||||
[`keypress`](keypress) subpackage stops it on any keystroke:
|
||||
|
||||
```go
|
||||
func runWatch(ctx context.Context, c Client, _ []string) error {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
go keypress.WaitForKey(ctx, cancel) // any key cancels ctx
|
||||
stream, _ := c.Watch(ctx, req)
|
||||
for { ev, err := stream.Recv(); /* returns when ctx is cancelled */ }
|
||||
}
|
||||
```
|
||||
|
||||
When stdin is not a terminal it simply waits on the context, so piped/one-shot
|
||||
use never blocks on a keypress.
|
||||
|
||||
## Output: color and JSON
|
||||
|
||||
A command describes its result **once** and the framework renders it:
|
||||
|
||||
Reference in New Issue
Block a user