feat(cli): --version/-V + in-app version command + release guard (ADR-0054)
Cargo.toml version is the single source of truth, surfaced by a --version/-V CLI flag and an in-app `version` command (both via cli::version_text -> cli.version_line). release.yaml gains a guard that fails the release unless the v* tag equals v<CARGO_PKG_VERSION>, keeping --version, the release name, and the asset in lockstep. New app command wired across grammar/REGISTRY/dispatch/usage/help/hint-corpus/keys; 6 test-first tests. Also fixes a stale "macOS deferred" comment in release.yaml. ADR-0054 + README index + plan-doc step 1.
This commit is contained in:
@@ -5,11 +5,15 @@
|
|||||||
# Matrix (D1, cross-built from Linux x86_64 via cargo-zigbuild):
|
# Matrix (D1, cross-built from Linux x86_64 via cargo-zigbuild):
|
||||||
# x86_64-unknown-linux-musl aarch64-unknown-linux-musl (static, D2)
|
# x86_64-unknown-linux-musl aarch64-unknown-linux-musl (static, D2)
|
||||||
# x86_64-pc-windows-gnu aarch64-pc-windows-gnullvm (standalone .exe)
|
# x86_64-pc-windows-gnu aarch64-pc-windows-gnullvm (standalone .exe)
|
||||||
# macOS is deferred — its arboard/AppKit link needs Apple's SDK (see ADR-ci-001).
|
# The two macOS targets are built separately by the dispatched
|
||||||
# D3 package-manager manifests layer on later.
|
# release-macos.yaml (native Tart runner; ADR-ci-003 amendment), uploading to
|
||||||
|
# the same release. D3 package-manager manifests layer on later.
|
||||||
#
|
#
|
||||||
# Tests run once (host) before the matrix, so a tag can never publish untested
|
# Tests run once (host) before the matrix, so a tag can never publish untested
|
||||||
# code, even one pointing at a commit that was never gated on a branch.
|
# code, even one pointing at a commit that was never gated on a branch. The
|
||||||
|
# version guard (ADR-0054) refuses to publish a tag whose vX.Y.Z disagrees with
|
||||||
|
# Cargo.toml's version, keeping `--version`, the release name, and the asset in
|
||||||
|
# lockstep.
|
||||||
name: release
|
name: release
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -23,6 +27,22 @@ jobs:
|
|||||||
image: git.lazyeval.net/oli/rdbms-playground-ci:latest
|
image: git.lazyeval.net/oli/rdbms-playground-ci:latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
- name: version guard — tag must equal Cargo.toml version (ADR-0054)
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
TAG: ${{ github.ref_name }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
# CARGO_PKG_VERSION is the single source of truth; the binary reports
|
||||||
|
# it via --version / the `version` command. Parse it from cargo
|
||||||
|
# metadata (node is in the CI image; avoids assuming jq).
|
||||||
|
VER=$(nix develop -c cargo metadata --no-deps --format-version 1 \
|
||||||
|
| node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>process.stdout.write(JSON.parse(s).packages[0].version))')
|
||||||
|
echo "tag=$TAG cargo=$VER"
|
||||||
|
if [ "$TAG" != "v$VER" ]; then
|
||||||
|
echo "ERROR: release tag '$TAG' != 'v$VER' (Cargo.toml). Bump Cargo.toml to the release version, commit, then retag (ADR-0054)." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
- name: test
|
- name: test
|
||||||
run: nix develop -c cargo test --no-fail-fast
|
run: nix develop -c cargo test --no-fail-fast
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
# ADR-0054: Release versioning policy + version surfaces (`--version` / `version`)
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Accepted — **implemented 2026-06-16** (plan:
|
||||||
|
`docs/plans/20260616-public-availability.md`, step 1). First step on the
|
||||||
|
road to public availability. Adds a `--version` / `-V` CLI flag and an
|
||||||
|
in-app `version` command, both reporting `CARGO_PKG_VERSION`, plus a
|
||||||
|
release-CI guard that the `v*` tag equals that version. No prior issue or
|
||||||
|
`requirements.md` item existed for this — it was an untracked gap.
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
Before this, `Cargo.toml` carried `version = "0.1.0"`, the binary exposed
|
||||||
|
**no** way to report its version, and `release.yaml` named assets from the
|
||||||
|
**git tag** (`rdbms-playground-<tag>-<target>`) while building from
|
||||||
|
`Cargo.toml`. Tag and crate version were **decoupled**: tagging `v0.2.0`
|
||||||
|
would publish an asset named `…-v0.2.0-…` containing a binary that (had it
|
||||||
|
been able to say) reported `0.1.0`. On the way to public availability —
|
||||||
|
where users download a versioned artifact and file bug reports against "the
|
||||||
|
version I'm running" — that drift is a correctness problem.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
1. **`Cargo.toml` `version` is the single source of truth.** This is the
|
||||||
|
idiomatic Rust position and avoids making `Cargo.toml` lie. The version
|
||||||
|
is read at compile time via `env!("CARGO_PKG_VERSION")`; no build-time
|
||||||
|
injection of the tag into the crate.
|
||||||
|
|
||||||
|
2. **Two user-facing surfaces, one source:**
|
||||||
|
- **`--version` / `-V`** — CLI flag (hand-rolled parser, mirrors
|
||||||
|
`--help`): prints and exits before any other work (`main.rs`).
|
||||||
|
- **`version`** — an in-app app command (REGISTRY node `app::VERSION`,
|
||||||
|
`AppCommand::Version`), emitting the same line into the output panel.
|
||||||
|
Both go through `cli::version_text()` → the catalog key
|
||||||
|
`cli.version_line` (`"rdbms-playground {version}"`), so there is exactly
|
||||||
|
one rendered string and one version source.
|
||||||
|
|
||||||
|
3. **Release-CI discipline.** `release.yaml`'s pre-build `test` job gains a
|
||||||
|
**version guard**: it parses `CARGO_PKG_VERSION` from `cargo metadata`
|
||||||
|
and **fails the release** unless the pushed tag equals `v<that version>`.
|
||||||
|
So `--version`, the release name, and the downloaded asset are always in
|
||||||
|
lockstep — enforced by the machine, not by memory.
|
||||||
|
|
||||||
|
4. **The release ritual:** bump `Cargo.toml` → commit → tag `v<that
|
||||||
|
number>` → push the tag. The guard rejects any deviation.
|
||||||
|
|
||||||
|
### Rejected / deferred
|
||||||
|
- **Inject the tag into the build** (tag as source of truth): fiddly with
|
||||||
|
cargo and makes `Cargo.toml` a placeholder/lie. Rejected.
|
||||||
|
- **Git-hash + build-date enrichment** (a `build.rs` so dev builds read
|
||||||
|
`0.2.0 (a1b2c3d)`): useful for bug reports, but not needed for the
|
||||||
|
tag↔release↔`--version` consistency this ADR is about. Deferred; can be
|
||||||
|
added behind the same `version_text()` seam without changing the policy.
|
||||||
|
- **UI placement beyond the `version` command** (status-bar string, etc.):
|
||||||
|
the command + `help` listing is enough for now (user decision).
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- A release can no longer ship a binary whose self-reported version
|
||||||
|
disagrees with its tag/filename.
|
||||||
|
- Cutting a release now *requires* a `Cargo.toml` bump commit — a small,
|
||||||
|
deliberate step (and a natural place to update a changelog later).
|
||||||
|
- New keys: `cli.version_line` (+ `help.app.version`, `parse.usage.version`,
|
||||||
|
`hint.cmd.version.what`/`.example`); a new REGISTRY command means the
|
||||||
|
comprehensiveness coverage test now also requires `hint.cmd.version`,
|
||||||
|
which is supplied. Tested: CLI flag parse (`--version`/`-V`/default-off),
|
||||||
|
`version_text()` carries `CARGO_PKG_VERSION`, the in-app command parses to
|
||||||
|
`AppCommand::Version` and emits the version.
|
||||||
|
- This is step 1 of `docs/plans/20260616-public-availability.md`; the
|
||||||
|
installer (`install.sh`) and package-manager work (D3) build on top.
|
||||||
@@ -66,3 +66,4 @@ This directory contains the project's ADRs, recorded per
|
|||||||
- [ADR-0051 — Bottom keybinding strip: context- and state-aware](0051-context-state-aware-keybinding-strip.md) — **Accepted + implemented 2026-06-13 (issue #27)**, closes Gitea **#27**. Repurposes the bottom status line into a **keystrokes-only, state-selected** strip (builds on ADR-0046 nav focus, ADR-0003 modes, ADR-0049 the #29 readline keys it now advertises, ADR-0022 the completion memo). A pure `status_bar_bindings(app) -> Vec<(key,label)>` chooses the strip by **priority, first match wins**: (1) **sidebar focus** → `Ctrl-O next pane · ↑↓/PgUp/PgDn scroll · Esc input`; (2) **completion memo live** (`last_completion`) → `Tab/Shift-Tab cycle · Esc cancel · Enter run`; (3) **history navigation** (new `App::is_browsing_history()` exposing the private `history_cursor`) → `↑↓ browse · Esc clear · Enter run`; (4) **editing** (input non-empty) → `Esc clear · Ctrl-A/E home/end · Ctrl-W del word · Enter run` (surfaces the #29 keys, closing ADR-0049's deferred advertisement); (5) **default** (empty) → `Ctrl-O sidebar · Tab complete · ↑ history · Enter run`. Priority is correct because Up clears the completion memo and Tab cancels history nav, so states 2/3 never co-occur, and the five are exhaustive for Input focus. **Typed-command words leave the strip** (`mode advanced`/`mode simple` switch, `:` one-shot) and **mode discovery moves to the empty-input hint** (`resolve_hint_lines`), **simple mode only**: `\`mode advanced\` for SQL` (the verb "type" omitted — the prompt implies it; advanced mode shows **no** pointer per a post-trial user decision — a switcher knows how they got there and `help` covers the way back). The one-shot's old `Backspace cancel one-shot` label is subsumed by the editing state (behaviour intact). Forks all user-chosen: **editing state shows the #29 keys** (vs unadvertised); **`Ctrl-C quit` omitted** from the strip (vs always shown); **no width-drop machinery** — the longest strip (~65 cols) fits all supported widths, so a **width-budget unit test** keeps it lean by construction instead (the user's own observation). Catalog: 12 new `shortcut.*` labels + the `panel.hint_mode_advanced` string added to `en-US.yaml`+`keys.rs` (validator-checked 1:1), 5 now-dead strip strings removed. **Modal-aware strip is OOS** (pre-existing: a modal owns the keyboard and carries its own hints; the strip under it is unchanged-in-kind, not worsened). Tests: 9 Tier-1 unit (per-state key sets — completion/history driven through real key events; width budget; mode-pointer presence/absence), 1 Tier-3 rewritten (`status_bar_is_keystroke_only_and_state_aware`), 15 full-panel snapshots re-accepted (reviewed — strip/hint only). **2467 pass / 0 fail / 0 skip (1 ignored), clippy clean.** OOS: modal-aware strip; a full-key cheatsheet overlay; Ctrl-K/U advertisement (editing strip shows the highest-value subset within the width budget)
|
- [ADR-0051 — Bottom keybinding strip: context- and state-aware](0051-context-state-aware-keybinding-strip.md) — **Accepted + implemented 2026-06-13 (issue #27)**, closes Gitea **#27**. Repurposes the bottom status line into a **keystrokes-only, state-selected** strip (builds on ADR-0046 nav focus, ADR-0003 modes, ADR-0049 the #29 readline keys it now advertises, ADR-0022 the completion memo). A pure `status_bar_bindings(app) -> Vec<(key,label)>` chooses the strip by **priority, first match wins**: (1) **sidebar focus** → `Ctrl-O next pane · ↑↓/PgUp/PgDn scroll · Esc input`; (2) **completion memo live** (`last_completion`) → `Tab/Shift-Tab cycle · Esc cancel · Enter run`; (3) **history navigation** (new `App::is_browsing_history()` exposing the private `history_cursor`) → `↑↓ browse · Esc clear · Enter run`; (4) **editing** (input non-empty) → `Esc clear · Ctrl-A/E home/end · Ctrl-W del word · Enter run` (surfaces the #29 keys, closing ADR-0049's deferred advertisement); (5) **default** (empty) → `Ctrl-O sidebar · Tab complete · ↑ history · Enter run`. Priority is correct because Up clears the completion memo and Tab cancels history nav, so states 2/3 never co-occur, and the five are exhaustive for Input focus. **Typed-command words leave the strip** (`mode advanced`/`mode simple` switch, `:` one-shot) and **mode discovery moves to the empty-input hint** (`resolve_hint_lines`), **simple mode only**: `\`mode advanced\` for SQL` (the verb "type" omitted — the prompt implies it; advanced mode shows **no** pointer per a post-trial user decision — a switcher knows how they got there and `help` covers the way back). The one-shot's old `Backspace cancel one-shot` label is subsumed by the editing state (behaviour intact). Forks all user-chosen: **editing state shows the #29 keys** (vs unadvertised); **`Ctrl-C quit` omitted** from the strip (vs always shown); **no width-drop machinery** — the longest strip (~65 cols) fits all supported widths, so a **width-budget unit test** keeps it lean by construction instead (the user's own observation). Catalog: 12 new `shortcut.*` labels + the `panel.hint_mode_advanced` string added to `en-US.yaml`+`keys.rs` (validator-checked 1:1), 5 now-dead strip strings removed. **Modal-aware strip is OOS** (pre-existing: a modal owns the keyboard and carries its own hints; the strip under it is unchanged-in-kind, not worsened). Tests: 9 Tier-1 unit (per-state key sets — completion/history driven through real key events; width budget; mode-pointer presence/absence), 1 Tier-3 rewritten (`status_bar_is_keystroke_only_and_state_aware`), 15 full-panel snapshots re-accepted (reviewed — strip/hint only). **2467 pass / 0 fail / 0 skip (1 ignored), clippy clean.** OOS: modal-aware strip; a full-key cheatsheet overlay; Ctrl-K/U advertisement (editing strip shows the highest-value subset within the width budget)
|
||||||
- [ADR-0052 — Mode-tagged history for cross-mode recall](0052-mode-tagged-history-cross-mode-recall.md) — **Accepted + implemented 2026-06-13 (issue #30)**, closes Gitea **#30** — the feature (advanced history reusable in simple mode) **and** the bug in its comment (the `:` one-shot prefix lost across sessions). **Amends ADR-0034** (status field gains a `:adv` tag; **journaling moves from the worker to the dispatch layer**), **ADR-0015 §5/§6** (history.log leaves the worker transaction — `commit-db-last` now scopes yaml/csv/db only), and **ADR-0040** (a success-path journal-write failure is best-effort, not fatal); references ADR-0003. **Root cause:** history carried no mode, and the in-memory ring stored the raw `:select 1` while the worker journalled the *stripped* `select 1`, so the `:` was lost on disk. **Fix:** record the submission mode per entry as a **`:adv` suffix on the status token** (`ok`/`ok:adv`/`err`/`err:adv`) — `source` stays last + canonical so replay is unaffected; the in-memory ring (still `Vec<String>`) stores advanced entries in their `: `-prefixed simple-mode runnable form (a leading `:` unambiguously marks advanced since simple DSL never starts with `:`); recall **strips the `:` in advanced mode** (runs as bare SQL) and keeps it in simple mode (runs via the one-shot escape); hydration reconstructs the `: `-prefix from the tag, so cross-session = in-session. **The architectural turn (user's call):** the first draft kept journaling in the worker + threaded the mode down (~30-site plumbing); on review the user asked why the journal is written deep in the worker when the *failure* path already journals at the top of the chain — it shouldn't (history.log is a journal, not state). So **success journaling moved up** to `spawn_dsl_dispatch` / `run_replay` / the app-command sites (next to the failure path), the worker's `finalize_persistence` now writes only yaml/csv, and the journal write became **best-effort** (the command is already committed — consistent with the failure path; a rare disk-full leaves a committed command unjournalled, state intact). **App commands** journal simple (dispatched outside the spawn) and `submit` excludes them from the ring's advanced flag, so `undo`/`mode advanced` recall bare. Forks user-chosen: status-tag format (vs 4th field / `:`-in-source); unified scope; **dispatch-layer best-effort journaling** (vs worker-coupled-fatal). Two `/runda` passes (the second drove the relocation + app-command exclusion). Tests: the 15 worker-level journaling tests retired (worker no longer journals — yaml/csv/operation checks kept), re-covered at the new layer (history.rs status-tag + `:`-reconstruct; app.rs recall matrix; the #30 cross-session regression in `iteration6`; replay tests cover `run_replay` journaling). **2471 pass / 0 fail / 0 skip (1 ignored), clippy clean.** replay re-journaling mode-fidelity (a replayed advanced line re-journals simple — not a regression). **Follow-up done 2026-06-14:** the vestigial worker `source` plumbing was fully unwound (compiler-guided, no behaviour change) — `_source` removed from `finalize_persistence`/`do_rebuild_from_text`, the three `*_request` wrappers inlined+deleted, the dead `source` param dropped from the ~30 forwarding worker handlers, and the `source` field removed from the `DescribeTable`/`QueryData`/`RunSelect` requests + their `DatabaseHandle` methods (~164 mostly-test call sites); the only worker `source` left is the snapshot/undo label (see ADR-0052 *Consequences*)
|
- [ADR-0052 — Mode-tagged history for cross-mode recall](0052-mode-tagged-history-cross-mode-recall.md) — **Accepted + implemented 2026-06-13 (issue #30)**, closes Gitea **#30** — the feature (advanced history reusable in simple mode) **and** the bug in its comment (the `:` one-shot prefix lost across sessions). **Amends ADR-0034** (status field gains a `:adv` tag; **journaling moves from the worker to the dispatch layer**), **ADR-0015 §5/§6** (history.log leaves the worker transaction — `commit-db-last` now scopes yaml/csv/db only), and **ADR-0040** (a success-path journal-write failure is best-effort, not fatal); references ADR-0003. **Root cause:** history carried no mode, and the in-memory ring stored the raw `:select 1` while the worker journalled the *stripped* `select 1`, so the `:` was lost on disk. **Fix:** record the submission mode per entry as a **`:adv` suffix on the status token** (`ok`/`ok:adv`/`err`/`err:adv`) — `source` stays last + canonical so replay is unaffected; the in-memory ring (still `Vec<String>`) stores advanced entries in their `: `-prefixed simple-mode runnable form (a leading `:` unambiguously marks advanced since simple DSL never starts with `:`); recall **strips the `:` in advanced mode** (runs as bare SQL) and keeps it in simple mode (runs via the one-shot escape); hydration reconstructs the `: `-prefix from the tag, so cross-session = in-session. **The architectural turn (user's call):** the first draft kept journaling in the worker + threaded the mode down (~30-site plumbing); on review the user asked why the journal is written deep in the worker when the *failure* path already journals at the top of the chain — it shouldn't (history.log is a journal, not state). So **success journaling moved up** to `spawn_dsl_dispatch` / `run_replay` / the app-command sites (next to the failure path), the worker's `finalize_persistence` now writes only yaml/csv, and the journal write became **best-effort** (the command is already committed — consistent with the failure path; a rare disk-full leaves a committed command unjournalled, state intact). **App commands** journal simple (dispatched outside the spawn) and `submit` excludes them from the ring's advanced flag, so `undo`/`mode advanced` recall bare. Forks user-chosen: status-tag format (vs 4th field / `:`-in-source); unified scope; **dispatch-layer best-effort journaling** (vs worker-coupled-fatal). Two `/runda` passes (the second drove the relocation + app-command exclusion). Tests: the 15 worker-level journaling tests retired (worker no longer journals — yaml/csv/operation checks kept), re-covered at the new layer (history.rs status-tag + `:`-reconstruct; app.rs recall matrix; the #30 cross-session regression in `iteration6`; replay tests cover `run_replay` journaling). **2471 pass / 0 fail / 0 skip (1 ignored), clippy clean.** replay re-journaling mode-fidelity (a replayed advanced line re-journals simple — not a regression). **Follow-up done 2026-06-14:** the vestigial worker `source` plumbing was fully unwound (compiler-guided, no behaviour change) — `_source` removed from `finalize_persistence`/`do_rebuild_from_text`, the three `*_request` wrappers inlined+deleted, the dead `source` param dropped from the ~30 forwarding worker handlers, and the `source` field removed from the `DescribeTable`/`QueryData`/`RunSelect` requests + their `DatabaseHandle` methods (~164 mostly-test call sites); the only worker `source` left is the snapshot/undo label (see ADR-0052 *Consequences*)
|
||||||
- [ADR-0053 — Contextual `hint` command and keybinding](0053-contextual-hint-command-and-keybinding.md) — **Accepted, implemented 2026-06-15** (Phases A–D; closes **A1** + requirements **H2**). Settles the `hint` slot ADR-0003 left "ADR pending"; closes the last open piece of **A1** and tracks requirements **H2**. **Two surfaces:** an **F1 keybinding** that renders a deep hint for the *live* partial input without submitting (the primary path — a submitted `hint` command can't see the buffer it would help with, since Enter empties it), and a submitted **`hint` command** that expands on the *most recent error*. **No topic argument** (contextual only — `help <topic>` already owns explicit reference). Introduces a **tier-3 teaching layer**, deeper than the existing tier-1 (colour / error headline) and tier-2 (ambient one-liner; and the error `hint:`, which is shown **by default** since `Verbosity::Verbose` is the default — `messages short` is the opt-*out*); without it `hint` would just duplicate what's already on screen. Tier-3 content lives in the catalogue under `hint.cmd.<hint_id>` (per command form) and `hint.err.<class>` (per error/diagnostic class), each a structured `what`/`example`/`concept` block rendered via a new `note_hint*` family with `OutputStyleClass::Hint`. **Keyed per-form via a new `hint_ids: &[&str]` field on `CommandNode` mirroring `usage_ids`** (revised in Phase B): a per-*node* key proved too coarse — `add`/`drop`/`show`/`create` are each one node spanning many forms, and a live-input hint for `add 1:n relationship` must be specific to relationships; `hint_key_for_input_in_mode` reuses `usage_key_for_input_in_mode`'s form-word disambiguation, and covers the advanced-SQL forms whose `usage_ids` are empty. Not keyed off `help_id` (it is `None` on the advanced-SQL nodes purely to dedup the `help` list; that parallel gap is issue **#36**). **Clause-concept hints** (`on delete` actions, constraint slots, `with pk`, cardinality) are a recorded **deferred extension** (`hint.concept.<topic>`, issue **#37**) — per-form is the right tier-3 granularity, with position-awareness owned by tier-2 + the live `Next:` line. Runtime `translate_error` classes resolve via stored `last_error_hint_key` (`hint` command / empty-F1). (The second route — pre-submit `diagnostic.*` read live from the walker on the F1 path — is **deferred**, issue **#38**: `Diagnostic` carries no class key.) Adds `AppCommand::Hint`, a `HINT` grammar node + REGISTRY entry, the `hint_ids` field, and `last_error_hint_key`; F1 is a read-only overlay (buffer + completion memo untouched). **Content is the bulk of the work** (the mechanism is ~a day): v1 scope = ~37 command forms + 9 runtime error classes (comprehensive for those, ~57 blocks), authored **exemplars-first** (voice approved in this ADR's `/runda` review, then mass-authored in batches), enforced by a **comprehensiveness coverage test**, with graceful fall-back to tier-2 if a key is ever missing. The **pre-submit-diagnostic route + ~33 `diagnostic.*` blocks were deferred** (issue **#38**) — `Diagnostic` carries no class key, so the route needs a broad change for marginal value (tier-2 already surfaces diagnostics; many duplicate runtime classes). Forks user-chosen: two-surface model; **F1** (vs `?` / a chord); no-arg; comprehensive-for-commands-and-errors scope; exemplars-first; diagnostics deferred. OOS: per-topic `hint <topic>` (rejected — overlaps `help`); always-on tier-3 (rejected — keeps ambient terse); non-`en-US` locales + success-command teaching (deferred); clause-concept hints (issue #37); the diagnostic route (issue #38); the `help`-side advanced-SQL gap (issue #36)
|
- [ADR-0053 — Contextual `hint` command and keybinding](0053-contextual-hint-command-and-keybinding.md) — **Accepted, implemented 2026-06-15** (Phases A–D; closes **A1** + requirements **H2**). Settles the `hint` slot ADR-0003 left "ADR pending"; closes the last open piece of **A1** and tracks requirements **H2**. **Two surfaces:** an **F1 keybinding** that renders a deep hint for the *live* partial input without submitting (the primary path — a submitted `hint` command can't see the buffer it would help with, since Enter empties it), and a submitted **`hint` command** that expands on the *most recent error*. **No topic argument** (contextual only — `help <topic>` already owns explicit reference). Introduces a **tier-3 teaching layer**, deeper than the existing tier-1 (colour / error headline) and tier-2 (ambient one-liner; and the error `hint:`, which is shown **by default** since `Verbosity::Verbose` is the default — `messages short` is the opt-*out*); without it `hint` would just duplicate what's already on screen. Tier-3 content lives in the catalogue under `hint.cmd.<hint_id>` (per command form) and `hint.err.<class>` (per error/diagnostic class), each a structured `what`/`example`/`concept` block rendered via a new `note_hint*` family with `OutputStyleClass::Hint`. **Keyed per-form via a new `hint_ids: &[&str]` field on `CommandNode` mirroring `usage_ids`** (revised in Phase B): a per-*node* key proved too coarse — `add`/`drop`/`show`/`create` are each one node spanning many forms, and a live-input hint for `add 1:n relationship` must be specific to relationships; `hint_key_for_input_in_mode` reuses `usage_key_for_input_in_mode`'s form-word disambiguation, and covers the advanced-SQL forms whose `usage_ids` are empty. Not keyed off `help_id` (it is `None` on the advanced-SQL nodes purely to dedup the `help` list; that parallel gap is issue **#36**). **Clause-concept hints** (`on delete` actions, constraint slots, `with pk`, cardinality) are a recorded **deferred extension** (`hint.concept.<topic>`, issue **#37**) — per-form is the right tier-3 granularity, with position-awareness owned by tier-2 + the live `Next:` line. Runtime `translate_error` classes resolve via stored `last_error_hint_key` (`hint` command / empty-F1). (The second route — pre-submit `diagnostic.*` read live from the walker on the F1 path — is **deferred**, issue **#38**: `Diagnostic` carries no class key.) Adds `AppCommand::Hint`, a `HINT` grammar node + REGISTRY entry, the `hint_ids` field, and `last_error_hint_key`; F1 is a read-only overlay (buffer + completion memo untouched). **Content is the bulk of the work** (the mechanism is ~a day): v1 scope = ~37 command forms + 9 runtime error classes (comprehensive for those, ~57 blocks), authored **exemplars-first** (voice approved in this ADR's `/runda` review, then mass-authored in batches), enforced by a **comprehensiveness coverage test**, with graceful fall-back to tier-2 if a key is ever missing. The **pre-submit-diagnostic route + ~33 `diagnostic.*` blocks were deferred** (issue **#38**) — `Diagnostic` carries no class key, so the route needs a broad change for marginal value (tier-2 already surfaces diagnostics; many duplicate runtime classes). Forks user-chosen: two-surface model; **F1** (vs `?` / a chord); no-arg; comprehensive-for-commands-and-errors scope; exemplars-first; diagnostics deferred. OOS: per-topic `hint <topic>` (rejected — overlaps `help`); always-on tier-3 (rejected — keeps ambient terse); non-`en-US` locales + success-command teaching (deferred); clause-concept hints (issue #37); the diagnostic route (issue #38); the `help`-side advanced-SQL gap (issue #36)
|
||||||
|
- [ADR-0054 — Release versioning policy + version surfaces (`--version` / `version`)](0054-release-versioning-and-version-surfaces.md) — **Accepted + implemented 2026-06-16** (plan: `docs/plans/20260616-public-availability.md`, step 1 on the road to public availability; no prior issue/`requirements.md` item — an untracked gap). Fixes the **tag↔crate-version decoupling**: `Cargo.toml` built `0.1.0` while `release.yaml` named assets from the git tag, so a binary could report a version different from the asset it shipped in. **Decision:** `Cargo.toml` `version` is the **single source of truth** (read via `env!("CARGO_PKG_VERSION")`, no tag-injection); two surfaces report it through one `cli::version_text()` → catalog `cli.version_line` — a **`--version` / `-V`** CLI flag (mirrors `--help`, prints+exits in `main.rs`) and an in-app **`version`** command (REGISTRY node `app::VERSION`, `AppCommand::Version`, emits via `note_system`); and a **release-CI version guard** (`release.yaml` `test` job parses `cargo metadata` and **fails the release** unless the `v*` tag equals `v<CARGO_PKG_VERSION>`). Release ritual: bump `Cargo.toml` → commit → tag → push. New keys `cli.version_line` + `help.app.version` + `parse.usage.version` + `hint.cmd.version.{what,example}` (the new REGISTRY command pulls in the comprehensiveness coverage gate). Rejected: tag-as-source (makes Cargo.toml lie). Deferred: git-hash/build-date enrichment (behind the same `version_text()` seam); UI placement beyond the command. Tested test-first: CLI parse (`--version`/`-V`/default-off), `version_text()` carries `CARGO_PKG_VERSION`, the in-app command parses + emits. Also corrected a stale `release.yaml` header comment ("macOS is deferred" → built by the dispatched `release-macos.yaml`).
|
||||||
|
|||||||
+29
@@ -1868,6 +1868,12 @@ impl App {
|
|||||||
self.note_hint_for_recent_error();
|
self.note_hint_for_recent_error();
|
||||||
Vec::new()
|
Vec::new()
|
||||||
}
|
}
|
||||||
|
// ADR-0054: the in-app twin of `--version`. Reports the same
|
||||||
|
// single source of truth (`CARGO_PKG_VERSION`, via cli::version_text).
|
||||||
|
AppCommand::Version => {
|
||||||
|
self.note_system(crate::cli::version_text());
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
AppCommand::Rebuild => vec![Action::PrepareRebuild],
|
AppCommand::Rebuild => vec![Action::PrepareRebuild],
|
||||||
AppCommand::Save => self.handle_save_command(false),
|
AppCommand::Save => self.handle_save_command(false),
|
||||||
AppCommand::SaveAs => self.handle_save_command(true),
|
AppCommand::SaveAs => self.handle_save_command(true),
|
||||||
@@ -5737,6 +5743,29 @@ mod tests {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── ADR-0054: in-app `version` command ──────────────────────────
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn version_command_parses_to_app_version() {
|
||||||
|
use crate::dsl::{parse_command, AppCommand, Command};
|
||||||
|
assert!(matches!(
|
||||||
|
parse_command("version"),
|
||||||
|
Ok(Command::App(AppCommand::Version))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn version_command_emits_the_cargo_version() {
|
||||||
|
let mut app = App::new();
|
||||||
|
type_str(&mut app, "version");
|
||||||
|
submit(&mut app);
|
||||||
|
assert!(
|
||||||
|
output_contains(&app, env!("CARGO_PKG_VERSION")),
|
||||||
|
"in-app `version` should print CARGO_PKG_VERSION: {}",
|
||||||
|
error_lines(&app),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hint_command_with_no_recent_error_shows_getting_started() {
|
fn hint_command_with_no_recent_error_shows_getting_started() {
|
||||||
let mut app = App::new();
|
let mut app = App::new();
|
||||||
|
|||||||
+49
@@ -30,6 +30,10 @@ pub struct Args {
|
|||||||
/// `--help` / `-h`: print usage to stdout and exit. The
|
/// `--help` / `-h`: print usage to stdout and exit. The
|
||||||
/// runtime checks this flag before doing any other work.
|
/// runtime checks this flag before doing any other work.
|
||||||
pub help: bool,
|
pub help: bool,
|
||||||
|
/// `--version` / `-V`: print the version (`CARGO_PKG_VERSION`,
|
||||||
|
/// the single source of truth — ADR-0054) and exit. Checked
|
||||||
|
/// alongside `--help` before any other work.
|
||||||
|
pub version: bool,
|
||||||
/// `--no-undo`: disable the auto-snapshot / undo machinery for
|
/// `--no-undo`: disable the auto-snapshot / undo machinery for
|
||||||
/// this run (ADR-0006 Amendment 1). When set, no snapshots are
|
/// this run (ADR-0006 Amendment 1). When set, no snapshots are
|
||||||
/// taken — zero per-command overhead — and `undo` / `redo`
|
/// taken — zero per-command overhead — and `undo` / `redo`
|
||||||
@@ -62,6 +66,17 @@ pub fn help_text() -> String {
|
|||||||
crate::t!("help.cli_banner")
|
crate::t!("help.cli_banner")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Version line printed by `--version` / `-V` and the in-app `version`
|
||||||
|
/// command (ADR-0054).
|
||||||
|
///
|
||||||
|
/// `CARGO_PKG_VERSION` is the single source of truth — it equals the `v*`
|
||||||
|
/// release tag (the release CI guards that), so what the binary reports
|
||||||
|
/// always matches the downloaded artifact.
|
||||||
|
#[must_use]
|
||||||
|
pub fn version_text() -> String {
|
||||||
|
crate::t!("cli.version_line", version = env!("CARGO_PKG_VERSION"))
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ArgsError {
|
pub enum ArgsError {
|
||||||
MissingValue(&'static str),
|
MissingValue(&'static str),
|
||||||
@@ -129,6 +144,7 @@ impl Args {
|
|||||||
let mut project_path: Option<PathBuf> = None;
|
let mut project_path: Option<PathBuf> = None;
|
||||||
let mut resume = false;
|
let mut resume = false;
|
||||||
let mut help = false;
|
let mut help = false;
|
||||||
|
let mut version = false;
|
||||||
let mut no_undo = false;
|
let mut no_undo = false;
|
||||||
let mut mode: Option<Mode> = None;
|
let mut mode: Option<Mode> = None;
|
||||||
// Demonstration mode (ADR-0047): the env var is the default,
|
// Demonstration mode (ADR-0047): the env var is the default,
|
||||||
@@ -143,6 +159,9 @@ impl Args {
|
|||||||
"--help" | "-h" => {
|
"--help" | "-h" => {
|
||||||
help = true;
|
help = true;
|
||||||
}
|
}
|
||||||
|
"--version" | "-V" => {
|
||||||
|
version = true;
|
||||||
|
}
|
||||||
"--resume" => {
|
"--resume" => {
|
||||||
resume = true;
|
resume = true;
|
||||||
}
|
}
|
||||||
@@ -208,6 +227,7 @@ impl Args {
|
|||||||
project_path,
|
project_path,
|
||||||
resume,
|
resume,
|
||||||
help,
|
help,
|
||||||
|
version,
|
||||||
no_undo,
|
no_undo,
|
||||||
mode,
|
mode,
|
||||||
demo,
|
demo,
|
||||||
@@ -475,4 +495,33 @@ mod tests {
|
|||||||
let err = Args::parse(["--bogus", "/some/path"]).unwrap_err();
|
let err = Args::parse(["--bogus", "/some/path"]).unwrap_err();
|
||||||
assert!(matches!(&err, ArgsError::Unknown(s) if s == "--bogus"), "got: {err:?}");
|
assert!(matches!(&err, ArgsError::Unknown(s) if s == "--bogus"), "got: {err:?}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- ADR-0054: --version / -V ----
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn version_long_flag_parses() {
|
||||||
|
assert!(Args::parse(["--version"]).unwrap().version);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn version_short_flag_parses() {
|
||||||
|
assert!(Args::parse(["-V"]).unwrap().version);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn version_defaults_off() {
|
||||||
|
assert!(!Args::parse(std::iter::empty::<&str>()).unwrap().version);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn version_text_carries_the_cargo_version() {
|
||||||
|
// The binary's self-reported version IS Cargo.toml's (the
|
||||||
|
// single source of truth, ADR-0054) — and the release CI guards
|
||||||
|
// that the `v*` tag equals it.
|
||||||
|
let text = version_text();
|
||||||
|
assert!(
|
||||||
|
text.contains(env!("CARGO_PKG_VERSION")),
|
||||||
|
"version line should embed CARGO_PKG_VERSION; got {text:?}"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -557,6 +557,10 @@ pub enum AppCommand {
|
|||||||
/// (the buffer is empty post-submit). The live-input surface is
|
/// (the buffer is empty post-submit). The live-input surface is
|
||||||
/// the F1 keybinding, handled in `App::handle_key`, not here.
|
/// the F1 keybinding, handled in `App::handle_key`, not here.
|
||||||
Hint,
|
Hint,
|
||||||
|
/// Print the application version (ADR-0054): the in-app twin of the
|
||||||
|
/// `--version` / `-V` CLI flag. Emits `CARGO_PKG_VERSION` — the same
|
||||||
|
/// single source of truth — into the output panel.
|
||||||
|
Version,
|
||||||
/// Rebuild `playground.db` from `project.yaml` + data/, with
|
/// Rebuild `playground.db` from `project.yaml` + data/, with
|
||||||
/// confirmation modal.
|
/// confirmation modal.
|
||||||
Rebuild,
|
Rebuild,
|
||||||
@@ -1019,6 +1023,7 @@ impl Command {
|
|||||||
AppCommand::Quit => "quit",
|
AppCommand::Quit => "quit",
|
||||||
AppCommand::Help { .. } => "help",
|
AppCommand::Help { .. } => "help",
|
||||||
AppCommand::Hint => "hint",
|
AppCommand::Hint => "hint",
|
||||||
|
AppCommand::Version => "version",
|
||||||
AppCommand::Rebuild => "rebuild",
|
AppCommand::Rebuild => "rebuild",
|
||||||
AppCommand::Save => "save",
|
AppCommand::Save => "save",
|
||||||
AppCommand::SaveAs => "save as",
|
AppCommand::SaveAs => "save as",
|
||||||
|
|||||||
@@ -174,6 +174,10 @@ const fn build_rebuild(_path: &MatchedPath, _source: &str) -> Result<Command, Va
|
|||||||
Ok(Command::App(AppCommand::Rebuild))
|
Ok(Command::App(AppCommand::Rebuild))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fn build_version(_path: &MatchedPath, _source: &str) -> Result<Command, ValidationError> {
|
||||||
|
Ok(Command::App(AppCommand::Version))
|
||||||
|
}
|
||||||
|
|
||||||
const fn build_undo(_path: &MatchedPath, _source: &str) -> Result<Command, ValidationError> {
|
const fn build_undo(_path: &MatchedPath, _source: &str) -> Result<Command, ValidationError> {
|
||||||
Ok(Command::App(AppCommand::Undo))
|
Ok(Command::App(AppCommand::Undo))
|
||||||
}
|
}
|
||||||
@@ -294,6 +298,14 @@ pub static REBUILD: CommandNode = CommandNode {
|
|||||||
hint_ids: &["rebuild"],
|
hint_ids: &["rebuild"],
|
||||||
usage_ids: &["parse.usage.rebuild"],};
|
usage_ids: &["parse.usage.rebuild"],};
|
||||||
|
|
||||||
|
pub static VERSION: CommandNode = CommandNode {
|
||||||
|
entry: Word::keyword("version"),
|
||||||
|
shape: EMPTY_SEQ,
|
||||||
|
ast_builder: build_version,
|
||||||
|
help_id: Some("app.version"),
|
||||||
|
hint_ids: &["version"],
|
||||||
|
usage_ids: &["parse.usage.version"],};
|
||||||
|
|
||||||
pub static SAVE: CommandNode = CommandNode {
|
pub static SAVE: CommandNode = CommandNode {
|
||||||
entry: Word::keyword("save"),
|
entry: Word::keyword("save"),
|
||||||
shape: SAVE_AS_OPT,
|
shape: SAVE_AS_OPT,
|
||||||
|
|||||||
@@ -791,6 +791,7 @@ pub static REGISTRY: &[(&CommandNode, CommandCategory)] = &[
|
|||||||
(&app::QUIT, CommandCategory::Simple),
|
(&app::QUIT, CommandCategory::Simple),
|
||||||
(&app::HELP, CommandCategory::Simple),
|
(&app::HELP, CommandCategory::Simple),
|
||||||
(&app::HINT, CommandCategory::Simple),
|
(&app::HINT, CommandCategory::Simple),
|
||||||
|
(&app::VERSION, CommandCategory::Simple),
|
||||||
(&app::REBUILD, CommandCategory::Simple),
|
(&app::REBUILD, CommandCategory::Simple),
|
||||||
(&app::SAVE, CommandCategory::Simple),
|
(&app::SAVE, CommandCategory::Simple),
|
||||||
(&app::NEW, CommandCategory::Simple),
|
(&app::NEW, CommandCategory::Simple),
|
||||||
|
|||||||
@@ -181,6 +181,7 @@ pub const KEYS_AND_PLACEHOLDERS: &[(&str, &[&str])] = &[
|
|||||||
("help.app.quit", &[]),
|
("help.app.quit", &[]),
|
||||||
("help.app.help", &[]),
|
("help.app.help", &[]),
|
||||||
("help.app.hint", &[]),
|
("help.app.hint", &[]),
|
||||||
|
("help.app.version", &[]),
|
||||||
("help.app.rebuild", &[]),
|
("help.app.rebuild", &[]),
|
||||||
("help.app.save", &[]),
|
("help.app.save", &[]),
|
||||||
("help.app.new", &[]),
|
("help.app.new", &[]),
|
||||||
@@ -272,6 +273,8 @@ pub const KEYS_AND_PLACEHOLDERS: &[(&str, &[&str])] = &[
|
|||||||
("hint.cmd.help.concept", &[]),
|
("hint.cmd.help.concept", &[]),
|
||||||
("hint.cmd.hint.what", &[]),
|
("hint.cmd.hint.what", &[]),
|
||||||
("hint.cmd.hint.example", &[]),
|
("hint.cmd.hint.example", &[]),
|
||||||
|
("hint.cmd.version.what", &[]),
|
||||||
|
("hint.cmd.version.example", &[]),
|
||||||
("hint.cmd.rebuild.what", &[]),
|
("hint.cmd.rebuild.what", &[]),
|
||||||
("hint.cmd.rebuild.example", &[]),
|
("hint.cmd.rebuild.example", &[]),
|
||||||
("hint.cmd.rebuild.concept", &[]),
|
("hint.cmd.rebuild.concept", &[]),
|
||||||
@@ -486,6 +489,7 @@ pub const KEYS_AND_PLACEHOLDERS: &[(&str, &[&str])] = &[
|
|||||||
("parse.usage.mode", &[]),
|
("parse.usage.mode", &[]),
|
||||||
("parse.usage.new", &[]),
|
("parse.usage.new", &[]),
|
||||||
("parse.usage.quit", &[]),
|
("parse.usage.quit", &[]),
|
||||||
|
("parse.usage.version", &[]),
|
||||||
("parse.usage.rebuild", &[]),
|
("parse.usage.rebuild", &[]),
|
||||||
("parse.usage.redo", &[]),
|
("parse.usage.redo", &[]),
|
||||||
("parse.usage.replay", &[]),
|
("parse.usage.replay", &[]),
|
||||||
@@ -580,6 +584,7 @@ pub const KEYS_AND_PLACEHOLDERS: &[(&str, &[&str])] = &[
|
|||||||
("cli.multiple_paths", &["first", "second"]),
|
("cli.multiple_paths", &["first", "second"]),
|
||||||
("cli.resume_with_path", &[]),
|
("cli.resume_with_path", &[]),
|
||||||
("cli.unknown_argument", &["arg"]),
|
("cli.unknown_argument", &["arg"]),
|
||||||
|
("cli.version_line", &["version"]),
|
||||||
(
|
(
|
||||||
"archive.export_sequence_exhausted",
|
"archive.export_sequence_exhausted",
|
||||||
&["project", "target_dir", "limit"],
|
&["project", "target_dir", "limit"],
|
||||||
|
|||||||
@@ -162,6 +162,10 @@ error:
|
|||||||
# ---- Help text (CLI banner + in-app `help` command) ------------------
|
# ---- Help text (CLI banner + in-app `help` command) ------------------
|
||||||
# ---- CLI argument-parsing errors (stderr before TUI starts) ---------
|
# ---- CLI argument-parsing errors (stderr before TUI starts) ---------
|
||||||
cli:
|
cli:
|
||||||
|
# Version line for `--version` / `-V` and the in-app `version` command
|
||||||
|
# (ADR-0054). `{version}` is `CARGO_PKG_VERSION` — the single source of
|
||||||
|
# truth, equal to the `v*` release tag (release CI guards the match).
|
||||||
|
version_line: "rdbms-playground {version}"
|
||||||
missing_value: "missing value for --{flag}"
|
missing_value: "missing value for --{flag}"
|
||||||
invalid_value: "invalid value for --{flag}: {value} (expected one of: {expected})"
|
invalid_value: "invalid value for --{flag}: {value} (expected one of: {expected})"
|
||||||
unknown_argument: "unknown argument: {arg}"
|
unknown_argument: "unknown argument: {arg}"
|
||||||
@@ -186,6 +190,7 @@ help:
|
|||||||
|
|
||||||
Options:
|
Options:
|
||||||
-h, --help Print this help and exit.
|
-h, --help Print this help and exit.
|
||||||
|
-V, --version Print the version and exit.
|
||||||
--theme <light|dark> Override theme (default: auto-detect).
|
--theme <light|dark> Override theme (default: auto-detect).
|
||||||
--data-dir <PATH> Use PATH as the data root instead of
|
--data-dir <PATH> Use PATH as the data root instead of
|
||||||
the OS-standard location for this run.
|
the OS-standard location for this run.
|
||||||
@@ -210,6 +215,7 @@ help:
|
|||||||
|
|
||||||
App-level commands (typed inside the app, available in both modes):
|
App-level commands (typed inside the app, available in both modes):
|
||||||
quit Exit cleanly.
|
quit Exit cleanly.
|
||||||
|
version Print the application version.
|
||||||
mode simple|advanced Switch input mode.
|
mode simple|advanced Switch input mode.
|
||||||
help Show this list of commands in-app.
|
help Show this list of commands in-app.
|
||||||
save Save the current temp project under a
|
save Save the current temp project under a
|
||||||
@@ -258,6 +264,8 @@ help:
|
|||||||
help <command> — detailed help for one command (e.g. `help insert`)
|
help <command> — detailed help for one command (e.g. `help insert`)
|
||||||
hint: |-
|
hint: |-
|
||||||
hint — explain the most recent error (press F1 for a hint on what you're typing)
|
hint — explain the most recent error (press F1 for a hint on what you're typing)
|
||||||
|
version: |-
|
||||||
|
version — print the application version (same as the `--version` command-line flag)
|
||||||
rebuild: |-
|
rebuild: |-
|
||||||
rebuild — rebuild the project database from project.yaml + data/ (with confirmation)
|
rebuild — rebuild the project database from project.yaml + data/ (with confirmation)
|
||||||
save: |-
|
save: |-
|
||||||
@@ -425,6 +433,9 @@ hint:
|
|||||||
hint:
|
hint:
|
||||||
what: "Explain the most recent error — or, pressing F1 while typing, the command you're building."
|
what: "Explain the most recent error — or, pressing F1 while typing, the command you're building."
|
||||||
example: "hint"
|
example: "hint"
|
||||||
|
version:
|
||||||
|
what: "Print the application version."
|
||||||
|
example: "version"
|
||||||
rebuild:
|
rebuild:
|
||||||
what: "Rebuild the project database from its saved text files."
|
what: "Rebuild the project database from its saved text files."
|
||||||
example: "rebuild"
|
example: "rebuild"
|
||||||
@@ -874,6 +885,7 @@ parse:
|
|||||||
quit: "quit"
|
quit: "quit"
|
||||||
help: "help [<command>]"
|
help: "help [<command>]"
|
||||||
hint: "hint"
|
hint: "hint"
|
||||||
|
version: "version"
|
||||||
rebuild: "rebuild"
|
rebuild: "rebuild"
|
||||||
save: "save | save as"
|
save: "save | save as"
|
||||||
new: "new"
|
new: "new"
|
||||||
|
|||||||
+6
-1
@@ -1,6 +1,6 @@
|
|||||||
use std::process::ExitCode;
|
use std::process::ExitCode;
|
||||||
|
|
||||||
use rdbms_playground::cli::{help_text, Args};
|
use rdbms_playground::cli::{help_text, version_text, Args};
|
||||||
use rdbms_playground::{logging, runtime};
|
use rdbms_playground::{logging, runtime};
|
||||||
|
|
||||||
fn main() -> ExitCode {
|
fn main() -> ExitCode {
|
||||||
@@ -22,6 +22,11 @@ fn main() -> ExitCode {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if args.version {
|
||||||
|
println!("{}", version_text());
|
||||||
|
return ExitCode::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
if args.help {
|
if args.help {
|
||||||
print!("{}", help_text());
|
print!("{}", help_text());
|
||||||
return ExitCode::SUCCESS;
|
return ExitCode::SUCCESS;
|
||||||
|
|||||||
@@ -251,6 +251,7 @@ fn command_kind_label(cmd: &rdbms_playground::dsl::Command) -> String {
|
|||||||
AppCommand::Quit => "App(Quit)".into(),
|
AppCommand::Quit => "App(Quit)".into(),
|
||||||
AppCommand::Help { .. } => "App(Help)".into(),
|
AppCommand::Help { .. } => "App(Help)".into(),
|
||||||
AppCommand::Hint => "App(Hint)".into(),
|
AppCommand::Hint => "App(Hint)".into(),
|
||||||
|
AppCommand::Version => "App(Version)".into(),
|
||||||
AppCommand::Rebuild => "App(Rebuild)".into(),
|
AppCommand::Rebuild => "App(Rebuild)".into(),
|
||||||
AppCommand::Save => "App(Save)".into(),
|
AppCommand::Save => "App(Save)".into(),
|
||||||
AppCommand::SaveAs => "App(SaveAs)".into(),
|
AppCommand::SaveAs => "App(SaveAs)".into(),
|
||||||
|
|||||||
Reference in New Issue
Block a user